题解 P3747 【[六省联考2017]相逢是问候】

suncongbo

2019-07-29 23:11:31

Solution

看到这题题解都是什么线段树维护区间最小修改次数,我来发一波并查集+树状数组的做法。 因为每个数最多被修改$O(\log P)$次,所以对于修改次数达到$\log P$的,我们可以**直接跳过这一个点**。考虑一个暴力,每次修改从$l$到$r$枚举每个点,这样是$O(n^2)$的,但是我们如果能够在每个点$i$处修改完之后直接跳到下一个修改次数未达到上界的点,这样复杂度就是正确的。 考虑一个与BZOJ2054类似的思路,使用并查集来支持这个操作。初始时整个序列每个位置的父亲赋为本身,对于每个修改次数达到上界的点$i$,我们将$i$并到$i+1$上。那么点$i$的下一个未达到上界的点就是$i+1$所在联通块的根(这里一定要注意合并的有序性,要把左边往右边合并) 至于区间求和,树状数组即可。 但是由于本题时间复杂度瓶颈不在于数据结构,所以这对时间的优化可能并没有太大意义……但是至少好写了一些吧 附我的AC代码 ```cpp #include<cstdio> #include<cstdlib> #include<cstring> #include<cassert> #include<iostream> #define llong long long using namespace std; inline int read() { int x=0; bool f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=0; for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^'0'); if(f) return x; return -x; } const int N = 5e4; const int B = 20000; llong a[N+3]; llong b[N+3]; llong p[57]; int pos[N+3]; llong bit[N+3]; int uf[N+3]; llong pw[2][B+3][57]; int pwf[2][B+3][57]; int n,q,m; llong c; bool flag; llong quickpow(llong x,llong y,int mod) { int y0 = y%B,y1 = y/B; llong ret = pw[0][y0][mod]*pw[1][y1][mod]; flag |= pwf[0][y0][mod]|pwf[1][y1][mod]|(ret>=p[mod]); return ret%p[mod]; } llong getphi(llong x) { llong ret = x; int i = 2; while(i*i<=x && x>1) { if(x%i==0) { ret = ret/i*(i-1); while(x%i==0) {x/=i;} } i++; } if(x>1) {ret = ret/x*(x-1);} return ret; } int findfa(int u) { int i = u; while(uf[u]!=u) {u = uf[u];} while(uf[i]!=u) { int j = uf[i]; uf[i] = u; i = j; } return u; } void addval(int u,llong x) { while(u<=n) { bit[u] = (bit[u]+x)%p[0]; u+=(u&(-u)); } } llong querysum(int rb) { llong ret = 0ll; while(rb) { ret = (ret+bit[rb])%p[0]; rb -= (rb&(-rb)); } return ret; } llong getval(int y,llong x) { llong ret = x; flag = ret>=p[y]?true:false; ret%=p[y]; if(flag) ret+=p[y]; for(int i=y-1; i>=0; i--) { ret = quickpow(c,ret,i); if(flag) ret+=p[i]; } return ret%p[0]; } int main() { scanf("%d%d%lld%lld",&n,&q,&p[0],&c); while(p[m]!=1) {m++; p[m] = getphi(p[m-1]);} m++; p[m] = 1; for(int i=1; i<=n; i++) {scanf("%lld",&a[i]); a[i] %= p[0]; b[i] = a[i]; addval(i,a[i]); uf[i] = i;} uf[n+1] = n+1; for(int i=0; i<=m; i++) { pw[0][0][i] = 1ll; for(int j=1; j<=B; j++) { pw[0][j][i] = pw[0][j-1][i]*c; pwf[0][j][i] = pwf[0][j-1][i]|(pw[0][j][i]>=p[i]); pw[0][j][i] %= p[i]; } pw[1][0][i] = 1ll; for(int j=1; j<=B; j++) { pw[1][j][i] = pw[1][j-1][i]*pw[0][B][i]; pwf[1][j][i] = pwf[1][j-1][i]|pwf[0][B][i]|(pw[1][j][i]>=p[i]); pw[1][j][i] %= p[i]; } } while(q--) { int opt,lb,rb; scanf("%d%d%d",&opt,&lb,&rb); if(opt==0) { for(int i=findfa(lb); i<=rb; i=findfa(i+1)) { pos[i]++; addval(i,p[0]-b[i]); b[i] = getval(pos[i],a[i]); addval(i,b[i]); if(pos[i]==m) {uf[i] = i+1;} } } else if(opt==1) { llong ans = (querysum(rb)-querysum(lb-1)+p[0])%p[0]; printf("%lld\n",ans); } } return 0; } ```