1. 程式人生 > >2018 10 02 校內模擬 字首和+二分+線段樹,廣義尤拉定理

2018 10 02 校內模擬 字首和+二分+線段樹,廣義尤拉定理

T1:聚會 party.cpp 【描述】

在成都的一條街道上,一共有 N 戶人家,每個家庭有 Xi 個人,他們和諧的生活在 一起,作為全國和諧街道,他們經常會小範圍組織活動,每次活動會選擇一戶作為聚點, 並要求某些家庭參加,為了方便通知,村長每次邀請位置連續的家庭。因為每戶人數不 同,每個家庭之間有一定距離,村長希望你計算出每次邀請的家庭的移動代價。第 i 個家 庭移動到家庭j的代價是: Xi*dis(i,j) (dis(i,j)表示i到j的距離) 村長一共安排了m次聚會,每次邀請[Li,Ri]的家庭參加 【輸入】 第一行兩個數表示 n,m 第二行n-1 個數,第 i個數表示第i 個家庭與第 i+1 個家庭的距離 Di

第三行n 個數,表示每個家庭的人數 Xi

之後 m行每行三個數 x l r,表示查詢要把區間 [l,r]的家庭移動到x點的代價和 【輸出】

對於每個詢問輸出一個數表示答案,對19260817取模 【輸入樣例】 5 5 2 3 4 5 1 2 3 4 5 1 1 5 3 1 5 2 3 3 3 3 3 1 5 5 【輸出樣例】 5 5 2 3 4 5 1 2 3 4 5 1 1 5 3 1 5 2 3 3 3 3 3 1 5 5 【子任務】 對於30%的資料, n,m≤1000 對於另外20%的資料,所有家庭間的距離都為 1 對於另外20%的資料,所有家庭人數都為 1 對於100%的資料 , n,m≤200000;Xi,Di <=2*10^9

分析: 我們可以發現,距離計算公式 Xidis(i,j) =Xi(|dis(i,1)-dis(j,1)|) =|Xidis(i,1)-Xidis(j,1)| 所以我們可以維護字首和來快速計算某個家庭到目標點的路程。 對於連續的一段家庭,有下面三種情況: 1.目標點在左邊,此時S=X[l] * dis(l,1)-X[l] * dis(pos,1)+ X[l+1]* dis(l+1,1)-X[l+1] * dis(pos,1)+…+X[r] * dis(r,1)-X[r] * dis(pos,1)=sigma(x*dis(x,1))-sigma(X)dis(pos,1) 2.目標點在右邊,形同情況1 3.目標點在中間,則情況1,2各考慮一次。 字首和維護sigma(X)和sigma(x

dis(x,1))

程式碼:

#include<bits/stdc++.h>   
using namespace std;  
typedef long long ll;   
const ll mod=19260817;  
const int N=200010;   
int n,m;  
ll d[N],a[N];   
ll apre[N],pre[N],sumpre[N];  
ll work(int x,int l,int r,bool left) 
{   
    if(l>r) return 0;  
    ll ans1=((apre[r]-apre[l-1])%mod+mod)%mod;  
    ans1=ans1*pre[x]%mod;  
    ll ans2=((sumpre[r]-sumpre[l-1])%mod+mod)%mod;  
    if(!left) swap(ans1,ans2);  
    return ((ans1-ans2)%mod+mod)%mod;  
}  
    
int main() 
{   
    scanf("%d%d",&n,&m);  
    for(int i=2;i<=n;i++) 
	{   
        scanf("%lld",&d[i]);  
        pre[i]=(pre[i-1]+(d[i]%=mod))%mod;  
    }   
    for(int i=1;i<=n;i++) 
	{   
        scanf("%lld",&a[i]);  
        apre[i]=(apre[i-1]+(a[i]%=mod))%mod;  
        sumpre[i]=(sumpre[i-1]+a[i]*pre[i]%mod)%mod;  
    }   
    for(int i=1,x,l,r;i<=m;i++) 
	{   
        scanf("%d%d%d",&x,&l,&r);  
        ll ans1=work(x,l,min(r,x-1),true);  
        ll ans2=work(x,max(l,x+1),r,false);  
        printf("%lld\n",(ans1+ans2)%mod);  
    }   
    return 0;  
}   

T2:矩陣分組 matrix.cpp 【描述】 有 N 行 M 列的矩陣,每個格子中有一個數字,現在需要你將格子的數字分為 A,B 兩部分 要求: 1、每個數字恰好屬於兩部分的其中一個部分 2、每個部分內部方塊之間,可以上下左右相互到達,且每個內部方塊之間可以相互到達, 且最多拐一次彎

如: AAAAA AAAAA AAAAA AABAA BaAAA AAABB ABBBA BBAAA AAABB AABAA BaAAA ABBBB AAAAA AAAAA BBBBB

   (1)       (2)       (3)   

其中(1)(2)是不允許的分法,(3)是允許的分法。在(2)中,a屬於 A區域,這兩個 a元素之間 互相到達,但是不滿足只拐一次彎到達。

問:對於所有合法的分組中,A 區域和 B 區域的極差,其中極差較大的一個區域最小值是 多少 提示:極差就是區域內最大值減去最小值。 【輸入】 第一行兩個正整數 n,m 接下來n 行,每行 m個自然數A_{i,j}表示權值 【輸出】 輸出一行表示答案 【輸入樣例】 4 4 1 12 6 11 11 4 2 14 10 1 9 20 4 17 13 10 【輸出樣例】 11

【樣例解釋】 1 12 6 11 11 4 2 14 10 1 9 20 4 17 13 10

分法不唯一,如圖是一種合法的分法。左邊部分極差 12-1=11,右邊一塊極差 20-10=10, 所以答案取這兩個中較大者 11。沒有別的分法,可以使答案更小。 【測試資料】 測試點 N,m範圍 1,2 n<=10,m<=10 3-4 n=1,m<=2000 5-7 n<=200,m<=200 8-10 n<=2000,m<=2000 所有權值1<=a_ij<=10^9

分析:我們可以二分答案,然後驗證 結論一:設A區域在左邊,對於A區域,每一行的長度是單調且連續。直觀看下圖 在這裡插入圖片描述 因為要保證只能拐一次彎。 結論二、A和B 可以互換,我們不妨設最大極值在A中,那麼A 這樣一個梯狀的區域的 頂角處於某個角落,我們不妨設為左上角,然後將矩陣做 3 次旋轉就可以考慮到所有情況。

程式碼:

#include<iostream>   
#include<cstdio>   
#include<cstring>   
#include<algorithm>   
#define LL long long int   
using namespace std;   
const int maxn = 2010,maxm = 100005,INF = 2000000000;   
    
inline int read(){   
     int out = 0,flag = 1;char c = getchar();   
     while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}   
     while (c >= 48 && c <= 57) {out = out * 10 + c - 48; c = getchar();}   
     return out * flag;   
 }   
     
 int n,m,A[4][maxn][maxn],gmax = -INF,gmin = INF;   
     
 void init(){   
     n = read();   
     m = read();   
     int x = 1,x1 = 1,x2 = n,x3 = m,y = 1,y1 = n,y2 = m,y3 = 1,t;   
     for (int i = 1; i <= n; i++){   
         for (int j = 1; j <= m; j++){   
             t = A[0][x][y++] = A[1][x1++][y1] = A[2][x2][y2--] = A[3][x3--][y3] = read();   
             if (t > gmax) gmax = t;   
             if (t < gmin) gmin = t;   
         }   
         x++; y = 1;   
         y1--; x1 = 1;   
         x2--; y2 = m;   
         y3++; x3 = m;   
     }   
 }   
     
 int endi[maxn];   
 bool Check(int u,int d){   
     if (u & 1) swap(n,m);   
     endi[0] = m;   
     for (int i = 1,j; i <= n; i++){   
         for (j = 1; j <= endi[i - 1]; j++)   
             if (gmax - A[u][i][j] > d)   
                 break;   
         endi[i] = j - 1;   

T3:序列維護 sequence.cpp 【描述】 沒有題面,非常友好 給出一個長度為 n的序列,每個位置有個數字 Ai,有2個操作: 1、區間修改,將[L,R]區間的數字加上一個數 2、區間查詢[l,r] 查詢 在這裡插入圖片描述 【輸入格式】 第一行兩個整數 n,m ,表示序列長度和運算元。 接下來一行, n個整數a_i 表示這個序列。 接下來m 行,可能是以下兩種操作之一: 1 l,r,x,表示區間[l,r] 加上x 2 l,r,p ,表示進行一次查詢,模數為 p 【輸出格式】 對於每個操作 2,輸出一行表示答案。 【輸入樣例1】 6 4 1 2 3 4 5 6 2 1 2 10000007 2 2 3 5 1 1 4 1 2 2 4 10 【輸出樣例1】 1 3 1 【輸入樣例2】 5 5 2 3 3 3 3 1 1 1 530739835 2 1 1 8356089 2 1 4 5496738 1 1 2 66050181 1 2 4 138625417 【輸出樣例2】 4306230 697527 【資料範圍】 測試點 n的範圍 m的範圍 特殊限制 1 n = 5 m = 5 a_i ≤3 2 n = 1000 m = 1000 查詢區間長度為 1 3 n = 100000 m = 100000 查詢區間長度為 1 4 n = 1000 m = 1000 查詢區間長度不大於 2 5 n = 100000 m = 100000 查詢區間長度不大於 2 6 n = 1000 m = 1000 a_i ≤2 7 n = 1000 m = 1000 p = 2 8 n = 100000 m = 100000 p = 2 無修改 9 n = 1000 m = 1000 p ≤100000 無修改 10 n = 500000 m = 500000m=500000 無

分析:20%單點查詢 20%:兩個單點查詢然後快速冪(注意快速冪是先%不然會爆long long) 20%:單點查詢判斷奇偶性 點1和點6:暴力 100%:線段樹+廣義尤拉定理

在這裡插入圖片描述 所以我們先預處理出尤拉函式 如何判斷指數大於等於φ(p)? 只需要列舉後面 5 項或到第一個 1 即可,這裡暴力計算,當大於等於φ(p)時退出。(因為22222>=2*10^7 , 1^n=1) 當指數大於等於φ(p),遞迴計算 a[l] ^ (l+1,r) %φ(p)+ φ(p)

程式碼:

#include<bits/stdc++.h>   
const int N=500005;   
const int M=20000005;   
const int inf=N-4;   
const int lim=20000000;   
typedef long long ll;   
using namespace std;   
int n,m,x,phi[M],prime[M],cnt;   
 ll c[N],a[N];bool vis[M];   
 int las[N];   
 inline int read(){   
     int f=1,x=0;char ch;   
     do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');   
     do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');   
     return f*x;   
 }   
 inline int fpow(int x,ll p,int yql){   
     int ans=1;   
     for(;p;p>>=1,x=1LL*x*x%yql)if(p&1)ans=1LL*ans*x%yql;   
     return ans;   
 }   
 inline void calcpri(){   
     phi[1]=1;   
     for(int i=2;i<=lim;i++){   
         if(!vis[i])prime[++cnt]=i,phi[i]=i-1;   
         for(int j=1;j<=cnt;j++){   
             int t=i*prime[j];if(t>lim)break;   
             vis[t]=1;   
             if(i%prime[j]==0){phi[t]=phi[i]*prime[j];break;}   
             phi[t]=phi[i]*(prime[j]-1);   
         }   
     }   
 }   
 ll sumv[N<<2],addv[N<<2],fone[N<<2];   
 #define lson (o<<1)   
 #define rson (o<<1|1)   
 inline void pushup(int o){   
     sumv[o]=sumv[lson]+sumv[rson];   
     fone[o]=min(fone[lson],fone[rson]);   
 }   
 inline void build(int o,int l,int r){   
     fone[o]=inf;   
     if(l==r){sumv[o]=a[l];if(a[l]==1)fone[o]=l;return;}   
     int mid=(l+r)>>1;   
    build(lson,l,mid);build(rson,mid+1,r);   
    pushup(o);   
}   
inline void pushdown(int o,int l,int r){   
    if(!addv[o])return;   
    int mid=(l+r)>>1;   
    addv[lson]+=addv[o];addv[rson]+=addv[o];   
    sumv[lson]+=addv[o]*1LL*(mid-l+1);   
    sumv[rson]+=addv[o]*1LL*(r-mid);   
    fone[lson]=inf;fone[rson]=inf;   
    addv[o]=0;   
}   
inline void change(int o,int l,int r,int q,int v){   
    if(l==r){sumv[o]=v;return;}   
    int mid=(l+r)>>1;   
    pushdown(o,l,r);   
    if(q<=mid)change(lson,l,mid,q,v);   
    else change(rson,mid+1,r,q,v);   
    pushup(o);   
}   
inline void optadd(int o,int l,int r,int ql,int qr,int v){   
    if(ql<=l&&r<=qr){addv[o]+=v;sumv[o]+=1LL*(r-l+1)*v;fone[o]=inf;return;}   
    int mid=(l+r)>>1;pushdown(o,l,r);   
    if(ql<=mid)optadd(lson,l,mid,ql,qr,v);   
    if(qr>mid)optadd(rson,mid+1,r,ql,qr,v);   
    pushup(o);   
}   
inline ll querysum(int o,int l,int r,int q){   
    if(l==r)return sumv[o];   
    int mid=(l+r)>>1;pushdown(o,l,r);   
    if(q<=mid)return querysum(lson,l,mid,q);   
    else return querysum(rson,mid+1,r,q);   
}   
inline ll query(int q){   
    if(las[q]==m)return a[q];   
    las[q]=m;   
    return a[q]=querysum(1,1,n,q);   
}   
inline int queryfst(int o,int l,int r,int ql,int qr){   
    if(ql<=l&&r<=qr)return fone[o];   
    int mid=(l+r)>>1;pushdown(o,l,r);   
    int ans=inf;   
    if(ql<=mid)ans=min(ans,queryfst(lson,l,mid,ql,qr));   
    if(qr>mid)ans=min(ans,queryfst(rson,mid+1,r,ql,qr));   
    return ans;   
}   
inline ll calc(int l,int r,int yql){   
    if(query(l)%yql==0)return 0;   
    if(yql==1)return 1;   
    if(l==r)return query(l)%yql+(query(l)>=yql)*yql;   
    int f=min(r,l+5);   
    for(int i=l+1;i<=f;i++)if(query(i)==1){f=i;break;}   
    ll last=query(f),q=0;   
    for(int i=f-1;i>=l+1;i--){   
          q=last,last=1;   
          while(q--){   
              last*=query(i);   
              if(last>=phi[yql])return fpow(query(l)%yql,calc(l+1,r,phi[yql])+phi[yql],yql);   
          }   
      }   
      return fpow(query(l)%yql,last,yql);   
  }   
  int main(){   
      calcpri();memset(las,-1,sizeof(las));   
      n=read();m=read();   
      for(int i=1;i<=n;i++)a[i]=read();   
      build(1,1,n);   
      while(m--){   
          int opt=read(),l=read(),r=read(),yql=read();   
          if(opt==1)optadd(1,1,n,l,r,yql);   
          else printf("%lld\n",(calc(l,r,yql))%yql);   
      }   
  }