bzoj 4869: [Shoi2017]相逢是问候

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 }
View Code

 

 

转载于:https://www.cnblogs.com/ZJXXCN/p/11505015.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值