来自FallDream的博客,未经允许,请勿转载,谢谢。
---------------------------------------------------
ditoly这次打好了虐爆我们的主意,掏出三道丧题,囊括三种赛制,第一道丧病oi题,第二道sb交互题,第三道奇怪提答............
第二道交互题没人做,也挺奇怪的,不讲了;
第三道是找规律,也就50个数列把,我拿出我最强的找规律水平,用上插值啊快速幂啊矩阵乘法啊肉眼观察法啊乱差分啊等等办法,骗到了48分,貌似全场最高了..得分靠随缘,也不讲了。
讲讲第一题,给定一个数列,需要支持100种操作。 $n,mleqslant 100000$
1:查询一段区间,你每次可以任选其中一段区间+1或者-1,求把它变成全是0的最小次数。
10:区间加一个值。 11:区间翻转 100:回到k次操作之前的状态。
看到这题,很明显可以可持久化平衡树,但我姿势不够,不会,而且丧病出题人卡空间。还好脑补了一种乱建边的方法,从一个状态向它能转移到的状态连边,然后dfs,离开时撤销,类似线段树分治。
然后维护一棵平衡树,修改操作容易维护,查询操作的答案是这个区间差分的绝对值之和加上左右端点的绝对值之和除以2. 我把查询做成了只能-1,爆0了qaq
#include<iostream> #include<cstdio> #define MN 100000 #define ll long long using namespace std; inline 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 s[MN+5],n,m,cnt=0; struct ques{int kind,l,r,x;ll ans;}q[MN+5]; struct edge{int to,next;}e[MN+5]; int fa[MN+5],c[MN+5][2],rt,head[MN+5],size[MN+5]; ll num[MN+5],ad[MN+5],nl[MN+5],nr[MN+5],sum[MN+5]; bool rev[MN+5]; template<typename t> t abs(t x){return x<0?-x:x;} inline void ins(int f,int t){e[++cnt]=(edge){t,head[f]};head[f]=cnt;} void update(int x) { int l=c[x][0],r=c[x][1]; nl[x]=(l?nl[l]:num[x]);nr[x]=(r?nr[r]:num[x]); size[x]=size[l]+size[r]+1; sum[x]=0; if(l) sum[x]+=sum[l]+abs(num[x]-nr[l]); if(r) sum[x]+=sum[r]+abs(num[x]-nl[r]); } inline void mark(int x,int z) { nl[x]+=z;nr[x]+=z; num[x]+=z;ad[x]+=z; } void pushdown(int x) { int l=c[x][0],r=c[x][1]; if(rev[x]) { rev[l]^=1;rev[r]^=1;rev[x]^=1; swap(c[l][0],c[l][1]);swap(c[r][0],c[r][1]); swap(nl[l],nr[l]);swap(nl[r],nr[r]); } if(ad[x]) { mark(l,ad[x]);mark(r,ad[x]); ad[x]=0; } } void build(int&x,int l,int r,int last) { if(l>r) return; x=l+r>>1;fa[x]=last;num[x]=s[x]; if(l==r) { size[x]=1;nl[x]=nr[x]=s[x];sum[x]=0; return; } build(c[x][0],l,x-1,x); build(c[x][1],x+1,r,x); update(x); } void rotate(int x,int&k) { int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1; if(y==k) k=x; else c[z][c[z][1]==y]=x; fa[x]=z;fa[y]=x;fa[c[x][r]]=y; c[y][l]=c[x][r];c[x][r]=y; update(y);update(x); } void splay(int x,int&k) { while(x!=k) { int y=fa[x],z=fa[y]; if(y!=k) { if(c[z][1]==y^c[y][1]==x) rotate(x,k); else rotate(y,k); } rotate(x,k); } } int find(int x,int rk) { pushdown(x); int sz=size[c[x][0]]+1; if(sz==rk) return x; else if(sz<rk) return find(c[x][1],rk-sz); else return find(c[x][0],rk); } int split(int l,int r) { splay(find(rt,l),rt);splay(find(rt,r),c[rt][1]); return c[c[rt][1]][0]; } void solve(int x) { if(q[x].kind==1) { ll y=sum[split(q[x].l,q[x].r+2)]; q[x].ans=(1LL*abs(num[find(rt,q[x].l+1)])+abs(num[find(rt,q[x].r+1)])+y)/2; } if(q[x].kind==10) { int y=split(q[x].l,q[x].r+2);mark(y,q[x].x); update(c[rt][1]);update(rt); } if(q[x].kind==11) { int y=split(q[x].l,q[x].r+2); rev[y]^=1;swap(c[y][0],c[y][1]); swap(nl[y],nr[y]); update(c[rt][1]);update(rt); } for(int i=head[x];i;i=e[i].next) solve(e[i].to); if(q[x].kind==10) { int y=split(q[x].l,q[x].r+2); mark(y,-q[x].x); update(c[rt][1]);update(rt); } if(q[x].kind==11) { int y=split(q[x].l,q[x].r+2); rev[y]^=1;swap(c[y][0],c[y][1]); swap(nl[y],nr[y]); update(c[rt][1]);update(rt); } } int main() { n=read();m=read(); for(int i=1;i<=n;i++)s[i+1]=read(); for(int i=1;i<=m;i++) { q[i].kind=read(); if(q[i].kind==1) q[i].l=read(),q[i].r=read(); if(q[i].kind==10) q[i].l=read(),q[i].r=read(),q[i].x=read(); if(q[i].kind==11) q[i].l=read(),q[i].r=read(); if(q[i].kind==100) q[i].x=read(),ins(i-q[i].x-1,i); else ins(i-1,i); } cnt=0;build(rt,1,n+2,0); solve(0); for(int i=1;i<=m;i++) if(q[i].kind==1) printf("%lld ",q[i].ans); return 0; }