[bzoj 4869] [六省联考2017] 相逢是问候
相逢是问候
2017-09-09
Description
Input
Output
Sample Input
1 2 3 4
0 1 4
1 2 4
0 1 4
1 1 3
Sample Output
3
1 40 19910626 2 0 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1
1 2 4 16 65536 11418102 18325590 13700558 13700558 13700558 13700558 13700558 13700558 13700558 13700558 13700558 13700558 13700558 13700558 13700558
考试时这个题感觉好神奇..当时某个学长讲的时候一脸mengbier,整个人都noip了..什么是φ?
欧拉函数裸题,一个数c在mod一个数的时候,在最多log2p次会不变....
所以线段树维护区间和,区间修改最小值;
修改时遍历到每一个节点,暴力修改每一个改变的值,因为到一定的次数不会变,所以记录一个tag代表这个区间一共修改了多少次...
当l==r时就是那个点修改次数...最多log2p次就不用改了,时间复杂度n*2log(n)。记住,欧拉不要乱mod,会出事的...
#include<iostream> #include<cstdlib> #include<cstdio> #include<algorithm> #define ll (long long) #define LL long long #define IN int using namespace std; const IN maxn=50000+99; const IN N=10000+99; int read(){ int an=0,f=1; char ch=getchar(); while(!('0'<=ch&&ch<='9')){if(ch=='-')f=-f;ch=getchar();} while('0'<=ch&&ch<='9'){an=an*10+ch-'0';ch=getchar();} return an*f; } IN phi[35],a[maxn],F; IN n,m,p,c; IN prime[maxn],isp[maxn],k,maxp;//k是质数个数,ips说明这是和数 struct saber{ IN sum,tag,l,r; }tr[maxn<<2]; IN Phi(IN x){ LL ans=x; for(IN i=1;i<=k && prime[i]*prime[i]<=x;i++){ if(!(x%prime[i]))ans=ans*(prime[i]-1)/prime[i]; while(!(x%prime[i]))x=x/prime[i]; } if(x>1)ans=ans/x*(x-1); return ans; } void euler(){ for(IN i=2;i<=N;i++){ if(!isp[i])k++,prime[k]=i; for(IN j=1;j<=k;j++){ if(i*prime[j]>N)break; isp[i*prime[j]]=1; if(!(i%prime[j]))break; } } phi[maxp]=p; while(phi[maxp]!=1)maxp++,phi[maxp]=Phi(phi[maxp-1]); maxp++; phi[maxp]=1; } void build(IN k,IN l,IN r){ tr[k].l=l; tr[k].r=r; if(l==r){ tr[k].sum=a[l]; return ; } IN mid=(l+r)>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); tr[k].sum=tr[k<<1].sum+tr[k<<1|1].sum; } IN ask(IN k,IN i,IN j){ IN l=tr[k].l,r=tr[k].r; if(l==i&&r==j)return tr[k].sum; IN mid=(l+r)>>1; if(mid>=j)return ask(k<<1,i,j); else if(i>mid)return ask(k<<1|1,i,j); else return (ask(k<<1,i,mid)+ask(k<<1|1,mid+1,j))%p; } IN qw(LL k,IN mod){ LL ans=1;LL s=c; while(k){ if(k&1)ans*=s; k>>=1; s*=s; if(s>=mod)F=1,s%=mod; if(ans>=mod)F=1,ans%=mod; } return ans%mod; } LL work(LL a,LL t){ LL tmp=a; if(tmp>phi[t])tmp=tmp%phi[t]+phi[t]; for(IN i=t;i>0;i--){ F=0;tmp=qw(tmp,phi[i-1]); if(F)tmp+=phi[i-1],F=0; } return tmp; } void change(IN k,IN i,IN j){ if(tr[k].tag>=maxp)return; LL l=tr[k].l,r=tr[k].r; if(l==r){ tr[k].tag++; tr[k].sum=work(a[l],tr[k].tag)%p; return; } IN mid=(l+r)>>1; if(mid>=j)change(k<<1,i,j); else if(i>mid)change(k<<1|1,i,j); else {change(k<<1,i,mid);change(k<<1|1,mid+1,j);} tr[k].sum=tr[k<<1].sum+tr[k<<1|1].sum; tr[k].tag=min(tr[k<<1].tag,tr[k<<1|1].tag); } int main(){ n=read();m=read();p=read();c=read(); for(IN i=1;i<=n;i++)a[i]=read(); euler(); build(1,1,n); while(m) {m--; IN x,y,z; x=read();y=read();z=read(); if(x){ printf("%lld\n",ask(1,y,z)%p); } else { change(1,y,z); } } return 0; }
by:s_a_b_e_r
表示考试结束学长讲题的时候一脸memgbier+1
欧拉定理+线段树维护
求ccc……a[i] %p的值
看上去一脸不可做,这要乘多少次
然而这个世界上存在着一个神奇的定理——欧拉定理EXT
ax ≡ax%φ(m)+φ(m)(mod m)
既然我们要求ccx(mod p)
不妨设d=cx 于是我们要求的就变成了cd
据欧拉定理有cd=cd%φ(p)+φ(p)(mod p)
即ccx=ccx%φ(p)+φ(p)(mod p)
看不清?放大一下
ccx=ccx%φ(p)+φ(p)(mod p)
标记的部分是不是很眼熟?
这部分又满足了欧拉定理
于是再抽一次φ变成
ccx%φ(φ(p))+φ(φ(p))%φ(p)+φ(p)(mod p)
这样一直抽下去,最后要%的东西会变成φ(φ(φ(φ(……p)))) (特别多个φ)
特别玄学的是这东西被φ多了就会变成1(据说最多log(p)次?)
然后就会出现x%1+1 = 0+1 = 1的状况
然后再对它修改就没什么用了,可以无视了
于是一发线段树维护每个点被修改了多少次
以及涉及欧拉的部分都不要乱%p,包括快速幂,容易出事qwq
#include<iostream> #include<cstdio> #define ll long long using namespace std; const int N=50099,SP=10099; int pri[N],cp,phi[50],ci; bool isp[N],f; int n,m,p,c,a[N]; struct tree{ int l,r,tag; ll sum; }t[N<<2]; void update(int k){ t[k].sum=t[k<<1].sum+t[k<<1|1].sum; t[k].tag=min(t[k<<1].tag,t[k<<1|1].tag); } void build(int k,int l,int r){ t[k].l=l,t[k].r=r; if(l==r){t[k].sum=a[l];return;} int mid=(l+r)>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); update(k); } int Phi(int x){//求欧拉函数 int ans=x; for(int i=1;i<=cp&&pri[i]*pri[i]<=x;++i) { if(!(x%pri[i]))ans=ans/pri[i]*(pri[i]-1); while(!(x%pri[i]))x/=pri[i]; } if(x>1)ans=ans/x*(x-1); return ans; } void prime(){//欧拉筛素数 for(int i=2;i<=SP;++i) { if(!isp[i])pri[++cp]=i; for(int j=1;j<=cp;++j) { if(i*pri[j]>N)break; isp[i*pri[j]]=1; if(i%pri[j]==0)break; } } phi[ci]=p; while(phi[ci]!=1)phi[++ci]=Phi(phi[ci-1]); phi[++ci]=1; } int kp(int x,int k,int mod){ int ans=1; while(k) { if(k&1){ if(1ll*ans*x>=mod)f=1; ans=1ll*ans*x%mod; } if(1ll*x*x>=mod)f=1; x=1ll*x*x%mod; k>>=1; } return ans; } int C(int a,int x){//抽φ int tmp=a; if(tmp>phi[x])tmp=tmp%phi[x]+phi[x];//欧拉定理前提条件 for(int i=x;i>0;--i) { f=0; tmp=kp(c,tmp,phi[i-1]); if(f==1)tmp+=phi[i-1]; } return tmp; } void change(int k,int L,int R){ if(t[k].tag>=ci)return; int l=t[k].l,r=t[k].r; if(l==r) { t[k].sum=C(a[l],++t[k].tag); return; } int mid=(l+r)>>1; if(L<=mid)change(k<<1,L,R); if(R>mid)change(k<<1|1,L,R); update(k); } ll query(int k,int L,int R){ int l=t[k].l,r=t[k].r; if(L<=l&&R>=r)return t[k].sum%p; if(r<L||l>R)return 0; return (query(k<<1,L,R)+query(k<<1|1,L,R))%p; } int main() { scanf("%d%d%d%d",&n,&m,&p,&c); for(int i=1;i<=n;++i)scanf("%d",&a[i]); build(1,1,n); prime(); for(int i=1;i<=m;++i) { int type,l,r; scanf("%d%d%d",&type,&l,&r); if(!type)change(1,l,r); else printf("%lld\n",query(1,l,r)%p); } return 0; }
by:wypx
s:省选后听见有人说谁mo谁是真***,今天我就在ans膜了个p,╮(╯▽╰)╭
w:这说明了什么(笑)
s:%数学dalao
w:果然数学题还是要放⑨啊w