Description
Informatikverbindetdichundmich.
信息将你我连结。B君希望以维护一个长度为n的数组,这个数组的下标为从1到n的正整数。一共有m个操作,可以
分为两种:0 l r表示将第l个到第r个数(al,al+1,...,ar)中的每一个数ai替换为c^ai,即c的ai次方,其中c是
输入的一个常数,也就是执行赋值ai=c^ai1 l r求第l个到第r个数的和,也就是输出:sigma(ai),l<=i<=rai因为
这个结果可能会很大,所以你只需要输出结果mod p的值即可。
Input
第一行有三个整数n,m,p,c,所有整数含义见问题描述。
接下来一行n个整数,表示a数组的初始值。
接下来m行,每行三个整数,其中第一个整数表示了操作的类型。
如果是0的话,表示这是一个修改操作,操作的参数为l,r。
如果是1的话,表示这是一个询问操作,操作的参数为l,r。
1 ≤ n ≤ 50000, 1 ≤ m ≤ 50000, 1 ≤ p ≤ 100000000, 0 < c <p, 0 ≤ ai < p
Output
对于每个询问操作,输出一行,包括一个整数表示答案mod p的值。
Sample Input
4 4 7 2
1 2 3 4
0 1 4
1 2 4
0 1 4
1 1 3
Sample Output
0
3
HINT
鸣谢多名网友提供正确数据,已重测!
思路: 这个题目也是扩展欧拉定理的应用,只不过和线段树结合起来了。
根据扩展欧拉定理的性质,我们知道题目的操作经过几次以后就会让每个ai变成定值(不超过log次),所以我们做区间修改的时候只要暴力修改就可以了。
也就是我们最多只要修改nlogn次,每次修改的时候都要用扩展欧拉定理重新求一下值,由于要涉及到快速幂,所以每次重新求值就需要log*log
这样总的时间复制度就是n*log^3。 比较容易被卡。
我们可以对幂次做预处理,这样时间复杂度可以优化到n*log^2,这样就很快了。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define LL long long 4 #define lc (x<<1) 5 #define rc (x<<1|1) 6 #define mid (l+r)/2 7 int const N=50000+10; 8 int c[40][10001],cc[40][10001],p,n,m,C,a[N],s[N<<2],sum[N<<2],phi[40],num; 9 int ph(int x){ 10 int ret=x; 11 for(int i=2;i*i<=x;i++) 12 if(x%i==0){ 13 ret=ret/i*(i-1); 14 while (x%i==0) x/=i; 15 } 16 if(x>1) ret=ret/x*(x-1); 17 return ret; 18 } 19 void build(int x,int l,int r){ 20 if(l==r){ 21 sum[x]=a[l]%phi[0]; return; 22 } 23 build(lc,l,mid); 24 build(rc,mid+1,r); 25 sum[x]=(sum[lc]+sum[rc])%phi[0]; 26 } 27 28 int calc(unsigned long long x,int p){ 29 int check=0; 30 int tx=x,tp=p; 31 // if(x>=phi[p+1]) x=x%phi[p+1]+phi[p+1]; 32 while (p>-1){ 33 if(check){ 34 x=x%phi[p+1]+phi[p+1]; 35 int num1=x/10000,num2=x%10000; 36 x=1LL*c[p][num2]*cc[p][num1]%phi[p]; 37 }else { 38 unsigned long long tmp=1; 39 if(C!=1) 40 for(int i=1;i<=x;i++){ 41 tmp=tmp*C; 42 if(tmp>=phi[p]) { 43 check=1; 44 int num1=x/10000,num2=x%10000; 45 tmp=1LL*c[p][num2]*cc[p][num1]%phi[p]; 46 break; 47 } 48 } 49 x=tmp; 50 } 51 p--; 52 } 53 //if(tp>=1) cout<<tx<<" **** "<<tp<<" "<<x<<endl; 54 return x; 55 } 56 void insert(int x,int l,int r,int ll,int rr){ 57 if(s[x]>num) return; 58 if(l==r){ 59 s[x]++; 60 sum[x]=calc(a[l],s[x]-1); 61 return; 62 } 63 if(ll<=mid) insert(lc,l,mid,ll,rr); 64 if(rr>mid) insert(rc,mid+1,r,ll,rr); 65 s[x]=min(s[lc],s[rc]); 66 sum[x]=(sum[lc]+sum[rc])%phi[0]; 67 } 68 int query(int x,int l,int r,int ll,int rr){ 69 if(ll<=l && r<=rr) return sum[x]; 70 int ret=0; 71 if(ll<=mid) ret+=query(lc,l,mid,ll,rr); 72 if(rr>mid) ret+=query(rc,mid+1,r,ll,rr); 73 return ret%phi[0]; 74 } 75 int main(){ 76 //freopen("9.in","r",stdin); 77 scanf("%d%d%d%d",&n,&m,&p,&C); 78 phi[0]=p; 79 while (1){ 80 c[num][0]=1; 81 unsigned long long tc=C; 82 for(int i=1;i<=10000;i++) c[num][i]=1LL*c[num][i-1]*C%p; 83 cc[num][0]=1; 84 for(int i=1;i<=10000;i++) cc[num][i]=1LL*cc[num][i-1]*c[num][10000]%p; 85 if(p==1) break; 86 phi[++num]=p=ph(p); 87 } 88 phi[++num]=1; 89 //cout<<num<<" "<<phi[0]<<" "<<phi[1]<<endl; 90 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 91 build(1,1,n); 92 while (m--){ 93 int opt,l,r; 94 scanf("%d%d%d",&opt,&l,&r); 95 if(opt==0)insert(1,1,n,l,r); 96 else printf("%d\n",query(1,1,n,l,r)); 97 //if(opt==0) break; 98 99 } 100 return 0; 101 }
原文地址:https://www.cnblogs.com/ZJXXCN/p/11505015.html