C. 「NOIP2021模拟赛 By ZJ C」自然溢出 题解

C. 「NOIP2021模拟赛 By ZJ C」自然溢出

solve

对于区间加一个值和减一个值我们都可以用树状数组或者线段树来维护,难点在于如何撤销操作

一次撤销操作可以看成回到某个历史节点继续操作,自然而然就想到一棵书,DFS树的同时维护线段树或者树状数组,回溯的时候反向操作就好了

code

#include<bits/stdc++.h>
#define int unsigned int
using namespace std;
const int maxn=1e6+5;
int c[2][maxn],a[maxn],N,son[maxn],lnk[maxn],nxt[maxn],cnt,M;
char s[15];
struct Q{
	int op,l,r,x;
}q[maxn];
inline int read(){
	int ret=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
	while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar();
	return ret*f;
}
void add(int op,int x,int y){for(;x<=N;x+=(x&-x))c[op][x]+=y;}
int get(int op,int x){int sum=0;for(;x;x-=(x&-x))sum+=c[op][x];return sum;}
int sum(int x){return x*get(0,x)-get(1,x);}
void update(int x,int y,int z){add(0,x,z),add(0,y+1,-z),add(1,x,(x-1)*z),add(1,y+1,-y*z);}
int query(int x,int y){return sum(y)-sum(x-1);}
inline int add_e(int x,int y){son[++cnt]=y;nxt[cnt]=lnk[x];lnk[x]=cnt;}
void dfs(int x){
	if(q[x].op==1)update(q[x].l,q[x].r,q[x].x);
	else if(q[x].op==3)q[x].x=query(q[x].l,q[x].r);
	for(int j=lnk[x];j;j=nxt[j])dfs(son[j]);
	if(q[x].op==1)update(q[x].l,q[x].r,-q[x].x);
}
signed main(){
	N=read();M=read();
	for(int i=1,last=0;i<=N;i++)a[i]=read(),add(0,i,a[i]-last),add(1,i,(i-1)*(a[i]-last)),last=a[i];
	for(int i=1;i<=M;i++){
		scanf("%s",s);
		if(s[0]=='A'){
			q[i].op=1;q[i].l=read();q[i].r=read();q[i].x=read();
			add_e(i-1,i);
		}
		else if(s[0]=='C'){
			q[i].op=2;q[i].x=read();
			add_e(i-q[i].x-1,i);
		}
		else {
			q[i].op=3;q[i].l=read();q[i].r=read();
			add_e(i-1,i);
		}
	}
	dfs(0);
	for(int i=1;i<=M;i++)if(q[i].op==3){
		printf("%u\n",q[i].x);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/martian148/p/15441320.html