bzoj 4869: [Shoi2017]相逢是问候 数论+线段树

xiaoxiao2021-02-27  180

题意

给出一个序列a,模数p和一个常数c,要求资瓷两个操作: 0 l r表示把l<=i<=r的a[i]变为 ca[i] 1 l r表示查询a[l..r]的和。 n,m<=50000,a[i],c,p<=10^9

分析

首先这题要用到欧拉定理EXT: x>=φ(p)cxcxmodφ(p)+φ(p) 因为注意到式子右边的次方上面又是一个相同形式的式子,我们可以用同样的方式递归处理。 那么我们就可以预处理出phi[i]表示p取i次phi后的值。因为一个数最多取O(logn)次phi后就会变为1,所以i最大为logp.然后最后还要多加一个1. 还有一个结论就是一个数在做常数次修改操作后就会变为不动点。 所以我们可以每次找到未被修改完的区间,然后每次暴力修改。 这样复杂度大概是O(nlog3n),在bzoj上能过。

这里还可以优化掉快速幂的那一个log,就是把一个数分成大于 216216 ,然后分别预处理出来,就可以O(1)做快速幂了。

代码

#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; typedef long long LL; const int N=50005; const int M=35; int n,m,phi[M],c,p1,a[N],table[32][1<<16][2]; struct tree{int s,tms;}t[N*5]; bool flag[32][1<<16][2]; int read() { int x=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int ksm(int x,int y,int MOD,bool &flag) { int ans=1;flag=0; while (y) { if (y&1) flag|=((LL)ans*x>=MOD),ans=(LL)ans*x%MOD; flag|=((LL)x*x>=MOD&&y>1);x=(LL)x*x%MOD;y>>=1; } return ans; } int bksm(int x,int y,bool &w) { int p=x&((1<<16)-1),q=x>>16; w=flag[y][p][0]|flag[y][q][1]|((LL)table[y][p][0]*table[y][q][1]>=phi[y]); return (LL)table[y][p][0]*table[y][q][1]%phi[y]; } int calc(int x,int p) { int ret=x;bool flag; if (ret>=phi[p]) ret=ret%phi[p]+phi[p]; while (p--) { x=ret; ret=bksm(x,p,flag); if (flag) ret+=phi[p]; } return ret%phi[0]; } int get_phi(int n) { int w=n,tmp=n; for (int i=2;i*i<=n;i++) if (tmp%i==0) { w=w/i*(i-1); while (tmp%i==0) tmp/=i; } if (tmp>1) w=w/tmp*(tmp-1); return w; } void updata(int d) { t[d].s=(t[d*2].s+t[d*2+1].s)%phi[0]; t[d].tms=min(t[d*2].tms,t[d*2+1].tms); } void build(int d,int l,int r) { if (l==r) { t[d].s=a[l]%phi[0]; return; } int mid=(l+r)/2; build(d*2,l,mid);build(d*2+1,mid+1,r); updata(d); } void modify(int d,int l,int r,int x,int y) { if (x>y||t[d].tms==p1) return; if (l==r) { t[d].tms++; t[d].s=calc(a[l],t[d].tms); return; } int mid=(l+r)/2; modify(d*2,l,mid,x,min(y,mid)); modify(d*2+1,mid+1,r,max(x,mid+1),y); updata(d); } int query(int d,int l,int r,int x,int y) { if (x>y) return 0; if (l==x&&r==y) return t[d].s; int mid=(l+r)/2; return (query(d*2,l,mid,x,min(y,mid))+query(d*2+1,mid+1,r,max(x,mid+1),y))%phi[0]; } void prework() { for (int i=0;i<=p1;i++) { table[i][0][0]=table[i][0][1]=1; if (phi[i]==1) flag[i][0][0]=flag[i][0][1]=1; int tmp=ksm(c,1<<16,phi[i],flag[i][1][1]),c1=ksm(c,1,phi[i],flag[i][1][0]); table[i][1][0]=c1;table[i][1][1]=tmp; for (int j=2;j<(1<<16);j++) { flag[i][j][0]=flag[i][j-1][0]|((LL)table[i][j-1][0]*c>=phi[i]); table[i][j][0]=(LL)table[i][j-1][0]*c%phi[i]; flag[i][j][1]=flag[i][j-1][1]|((LL)table[i][j-1][1]*tmp>=phi[i]); table[i][j][1]=(LL)table[i][j-1][1]*tmp%phi[i]; } } } int main() { n=read();m=read();phi[0]=read();c=read(); p1=0; while (phi[p1]>1) p1++,phi[p1]=get_phi(phi[p1-1]); phi[++p1]=1; prework(); for (int i=1;i<=n;i++) a[i]=read(); build(1,1,n); while (m--) { int op=read(),l=read(),r=read(); if (!op) modify(1,1,n,l,r); else printf("%d\n",query(1,1,n,l,r)); } return 0; }
转载请注明原文地址: https://www.6miu.com/read-9350.html

最新回复(0)