1 条题解
-
0
自动搬运
来自洛谷,原作者为

Leap_Frog
是啊……你就是那只鬼了……所以被你碰到以后,就轮到我变成鬼了搬运于
2025-08-24 22:02:35,当前版本为作者最后更新于2021-04-13 08:18:47,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
P.S.
高质量好数据结构。
一直拍挂
对拍建议 这样调试难度低一点 qwq。
目前最优解看人头 rank2,不过我这种写法应该会自带大常数。Description.
维护序列,要求支持:区间平方修改为模 ,查寻区间和。
Solution.
首先,我们发现,平方运算具有循环节。
打个表会发现题目中所有模下,循环节长度 最大值为 。
那么我们就可以考虑直接暴力,如果进入循环节了就直接开桶修改。
然后就做完了欸。
注意是区间修改,必须要打 lazetag。
注意无法预处理一个区间的答案,因为这个区间的子区间可能会被操作。
具体看代码及其注释吧Coding.
//是啊……你就是那只鬼了……所以被你碰到以后,就轮到我变成鬼了{{{ #include<bits/stdc++.h> using namespace std;typedef long long ll; template<typename T>inline void read(T &x) { x=0;char c=getchar(),f=0; for(;c<48||c>57;c=getchar()) if(!(c^45)) f=1; for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48); f?x=-x:x; }/*}}}*/ int n,m,P,a[100005],deg[10005];char v[10005]; struct segm{int smt,nw,sm[61],lz;char tag;}T[400005]; //tag代表当前区间是否全部进入循环节 //smt代表当前区间桶的循环节(为0代表未进入循环节 //sm表示桶,nw表示当前在桶中位置 //lz表示lazetag //如果未进入循环节 sm[0] 表示当前这个点的权值 inline int gcd(int a,int b) {return b?gcd(b,a%b):a;} inline int sqr(int x) {return x*x%P;} inline void pushup(int x) { T[x].tag=T[x<<1].tag&T[x<<1|1].tag; if(!T[x].smt) T[x].sm[0]=T[x<<1].sm[T[x<<1].nw]+T[x<<1|1].sm[T[x<<1|1].nw]; if(T[x<<1].smt&&T[x<<1|1].smt) { int nl=T[x<<1].smt,nr=T[x<<1|1].smt,lw=T[x<<1].nw,rw=T[x<<1|1].nw; T[x].smt=nl/gcd(nl,nr)*nr,T[x].nw=0;//公共循环节长度是 lcm for(int i=0;i<T[x].smt;i++) T[x].sm[i]=T[x<<1].sm[(i+lw)%nl]+T[x<<1|1].sm[(i+rw)%nr]; //把两个桶合并,注意两边不一定从起点开始,可能已经移动过了。 } } inline void allc(int x,int c) {T[x].nw=(T[x].nw+c)%T[x].smt,T[x].lz+=c;} inline void pushdw(int x) {if(T[x].lz) allc(x<<1,T[x].lz),allc(x<<1|1,T[x].lz),T[x].lz=0;} inline void INIT(int x) {//预处理一个结点的桶信息 for(int i=1;i<=60;i++) { T[x].sm[i]=sqr(T[x].sm[i-1]);//找到循环节 if(T[x].sm[i]==T[x].sm[0]) {T[x].smt=i;break;} } } inline void build(int x,int l,int r) {//无难点,不解释 if(l==r) return T[x].sm[T[x].smt=0]=a[l],void(); build(x<<1,l,(l+r)>>1),build(x<<1|1,((l+r)>>1)+1,r),pushup(x); } inline void modif(int x,int l,int r,int dl,int dr) { if(l>dr||dl>r) return;else if(dl>l||r>dr) return pushdw(x),modif(x<<1,l,(l+r)>>1,dl,dr),modif(x<<1|1,((l+r)>>1)+1,r,dl,dr),pushup(x); //如果小区间和大区间无包含关系,那就正常线段树处理。 if(T[x].tag) return allc(x,1),void();//如果已经进入循环节,直接打lazetag if(l^r) return pushdw(x),modif(x<<1,l,(l+r)>>1,dl,dr),modif(x<<1|1,((l+r)>>1)+1,r,dl,dr),pushup(x); //没进入循环节还不是叶结点,只能暴力向下 if(v[T[x].sm[0]]) return T[x].sm[0]=sqr(T[x].sm[0]),void(); //如果还在循环节外面,那就暴跳,否则就预处理了 if(!T[x].tag) INIT(x),T[x].tag=1,T[x].nw=1%T[x].smt;else T[x].nw=(T[x].nw+1)%T[x].smt; } inline int query(int x,int l,int r,int dl,int dr) {//无难点,不解释 if(dl>r||l>dr) return 0;else if(dl<=l&&r<=dr) return T[x].sm[T[x].nw];else pushdw(x); return query(x<<1,l,(l+r)>>1,dl,dr)+query(x<<1|1,((l+r)>>1)+1,r,dl,dr); } int main() { read(n),read(m),read(P);queue<int>q; for(int i=0;i<P;i++) deg[sqr(i)]++; for(int i=0;i<P;i++) if(!deg[i]) q.push(i); while(!q.empty()) { int x=q.front();q.pop(),v[x]=1; if(!--deg[sqr(x)]) q.push(sqr(x)); }//topo找环 for(int i=1;i<=n;i++) read(a[i]); for(build(1,1,n);m--;) { int op,l,r;read(op),read(l),read(r); if(op&1) modif(1,1,n,l,r);else printf("%d\n",query(1,1,n,l,r)); } return 0; }
- 1
信息
- ID
- 4024
- 时间
- 2000ms
- 内存
- 250MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者