[树剖] 树剖:从入门到进阶

题单

文章目录

  • 写在前面
  • T1 [Grass Planting G](https://www.luogu.com.cn/problem/P3038)
  • T2 [Max Flow P](https://www.luogu.com.cn/problem/P3128)
  • T3 [求和](https://www.luogu.com.cn/problem/P4427)
  • T4 [Cow Land G](https://www.luogu.com.cn/problem/P6098)
  • T5 [Tree](https://www.luogu.com.cn/problem/P4092)
  • T6 [Travel](https://www.luogu.com.cn/problem/P3976)
  • T7 [月下“毛景树”](https://www.luogu.com.cn/problem/P4315)
  • T8 [Magical Tree](https://www.luogu.com.cn/problem/P3833)
  • T9 [软件包管理器](https://www.luogu.com.cn/problem/P2146)

写在前面

树剖错了好多的地方:

  • 跳链的时候注意什么时候是 dep,什么时候是 dfn
  • DFS 的时候注意v==fa||v==son的时候不要随手 return
  • 但是在 DFS2 的时候 u 没有 sonreturn
  • 映射大法好!(前提是你实在整不出来了,那就狂加数组维护映射关系)
  • 蒟蒻现在才搞清楚维护是什么意思,至于树剖的维护,想清楚维护什么就行了

T1 Grass Planting G

显然树剖只能加作用于点权,于是想办法把边权转化为点权
考虑边权化点权:
把边权下放到儿子上去
于是区间修改,单点查询

  • 首先,读图,预DFS父亲深度子树大小重儿子
  • 然后,DFSDFS序重链顶
  • 最后,建 SGT,读操作
    1. 对于路径加边权,区间修改,注意不要把 LCA 加了
      方法:最后得到的 u , v u,v u,v中,若 d e p [ u ] < d e p [ v ] dep[u]dep[u]<dep[v],易知 u u uLCA,那么不加 u u u就行了,即 u p d a t e ( 1 , 1 , n , d f n [ u ] + 1 , d f n [ v ] ) update(1,1,n,dfn[u]+1,dfn[v]) update(1,1,n,dfn[u]+1,dfn[v])
    2. 对于路径求边权,单点查询,查就是了

注意两个函数: i s a l p h a ( ) , i s d i g i t ( ) isalpha(),isdigit() isalpha(),isdigit()要写对

#include
#define in Read()
using namespace std;
inline int in{
	int i=0,f=1;char ch;
	while(!isdigit(ch)&&ch!='-')ch=getchar();
	if(ch=='-')ch=getchar(),f=-1;
	while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

const int NNN=1e5+10;
int n,m,sz;
vector<int>G[NNN];
int fat[NNN];
int son[NNN];
int siz[NNN];
int top[NNN];
int dep[NNN];
int dfn[NNN],tot;
int sum[NNN<<2];
int laz[NNN<<2];
int wei[NNN<<2];

inline void push_up(int p){
	sum[p]=sum[p<<1]+sum[p<<1|1];
}

inline void push_down(int p,int len){
	laz[p<<1]+=laz[p];
	laz[p<<1|1]+=laz[p];
	sum[p<<1]+=laz[p]*(len-(len>>1));
	sum[p<<1|1]+=laz[p]*(len>>1);
	laz[p]=0;
}

inline void pre_DFS(int u,int fa){
	fat[u]=fa;
	dep[u]=dep[fa]+1;
	siz[u]=1;
	int heavy=0;
	for(int e=0;e<G[u].size();++e){
		int v=G[u][e];
		if(v==fa)continue;
		pre_DFS(v,u);
		siz[u]+=siz[v];
		if(heavy<siz[v])
			heavy=siz[v],son[u]=v;
	}
}

inline void DFS(int u,int pik){
	dfn[u]=++tot;
	top[u]=pik;
	if(!son[u])return;
	DFS(son[u],pik);
	for(int e=0;e<G[u].size();++e){
		int v=G[u][e];
		if(v^son[u]&&v^fat[u])
			DFS(v,v);
	}
}

inline void build(int p,int l,int r){
	if(l==r){
		sum[l]=0;
		return;
	}
	int mid=(l+r)>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	return;
}

inline void update(int p,int l,int r,int L,int R){
	if(L<=l&&r<=R){
		sum[p]+=r-l+1;
		laz[p]++;
		return;
	}
	push_down(p,r-l+1);
	int mid=(l+r)>>1;
	if(L<=mid) update(p<<1,l,mid,L,R);
	if(R>mid) update(p<<1|1,mid+1,r,L,R);
	push_up(p);
	return;
}

inline int query(int p,int l,int r,int k){
	if(l==r) return sum[p];
	push_down(p,r-l+1);
	int mid=(l+r)>>1,res=0;
	if(k<=mid) res+=query(p<<1,l,mid,k);
	else res+=query(p<<1|1,mid+1,r,k);
	return res;
}

inline void Modify(){
	int u=in,v=in;
	while(top[u]^top[v]){
		if(dep[top[u]]<dep[top[v]]) swap(u,v);
		update(1,1,n,dfn[top[u]],dfn[u]);
		u=fat[top[u]];
	}
	if(dep[u]>dep[v]) swap(u,v);
	update(1,1,n,dfn[u]+1,dfn[v]);
}

inline void Query(){
	int u=in,v=in;
	if(fat[v]==u) swap(u,v);
	printf("%d\n",query(1,1,n,dfn[u]));
	return;
}

int main(){
	n=in,m=in;
	for(int i=2;i<=n;++i){
		int u=in,v=in;
		G[u].push_back(v);
		G[v].push_back(u);
	}
	pre_DFS(1,0);
	DFS(1,1);
	build(1,1,n);
	for(int i=1;i<=m;++i){
		char opt=getchar();
		while(!isalpha(opt)) opt=getchar();
		if(opt=='P') Modify();
		else Query();
	}
	return 0;
}

T2 Max Flow P

什么鬼。。。最大流P
这两天考试的网络流已经够了,而且我还不会
幸好这是数据结构

不说了,区间修改,求整体最大值

  • 首先,读图,找重儿子父亲深度子树
  • 然后,giaoDFS序重链顶
  • 最后,建 SGTgiao 操作
    区间修改,直接返回线段树根节点的 val

写树状数组写炸了(其实可以写)

一个下午,重构三遍,堵死我也
经验:读图( n − 1 n-1 n1条边),跳链注意数组,注意边界与区间开闭

#include
#define in Read()
#define lch p<<1
#define rch p<<1|1
#define int long long
using namespace std;
inline int in{
	int i=0,f=1;char ch;
	while(!isdigit(ch)&&ch!='-')ch=getchar();
	if(ch=='-')ch=getchar(),f=-1;
	while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

const int NNN=1e5+10;
int n,m;
int first[NNN],nxt[NNN<<1],aim[NNN],etot;
int fat[NNN],son[NNN],top[NNN],siz[NNN],dfn[NNN],dep[NNN],tot;
struct Tree{
	int val,l,r,laz;
}tr[NNN<<2];

inline void add(int u,int v){
	nxt[++etot]=first[u];
	first[u]=etot;
	aim[etot]=v;
	return;
}

inline void DFS1(int fa,int u){
	fat[u]=fa,siz[u]=1,dep[u]=dep[fa]+1;
	for(int e=first[u];e;e=nxt[e]){
		int v=aim[e];
		if(v==fa) continue;
		DFS1(u,v);
		siz[u]+=siz[v];
		if(siz[son[u]]<siz[v]) son[u]=v;
	}
	return;
}

inline void DFS2(int u,int pik){
	top[u]=pik,dfn[u]=++tot;
	if(!son[u]) return;
	DFS2(son[u],pik);
	for(int e=first[u];e;e=nxt[e]){
		int v=aim[e];
		if(v==son[u]||v==fat[u]) continue;
		DFS2(v,v);
	}
	return;
}

inline void push_up(int p){
	tr[p].val=max(tr[lch].val,tr[rch].val);
	return;
}

inline void push_down(int p){
	if(!tr[p].laz) return;
	tr[lch].laz+=tr[p].laz;
	tr[rch].laz+=tr[p].laz;
	tr[lch].val+=tr[p].laz;
	tr[rch].val+=tr[p].laz;
	tr[p].laz=0;
	return;
}

inline void build(int p,int l,int r){
	tr[p].l=l,tr[p].r=r;
	if(l==r) return;
	int mid=(l+r)>>1;
	build(lch,l,mid);
	build(rch,mid+1,r);
	return;
}

inline void update(int p,int l,int r){
	if(l<=tr[p].l&&tr[p].r<=r){
		++tr[p].val;
		++tr[p].laz;
		return;
	}
	push_down(p);
	int mid=(tr[p].l+tr[p].r)>>1;
	if(l<=mid) update(lch,l,r);
	if(r>mid) update(rch,l,r);
	push_up(p);
	return;
}

inline void Modify(int u,int v){
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]]) swap(u,v);
		update(1,dfn[top[u]],dfn[u]);
		u=fat[top[u]];
	}
	if(dep[u]>dep[v]) swap(u,v);
	update(1,dfn[u],dfn[v]);
	return;
}

signed main(){
	n=in,m=in;
	for(int i=2;i<=n;++i){
		int u=in,v=in;
		add(u,v),add(v,u);
	}
	DFS1(0,1);
	DFS2(1,1);
	build(1,1,n);
	for(int i=1;i<=m;++i) Modify(in,in);
	printf("%lld\n",tr[1].val);
	return 0;
}

T3 求和

快速幂一波

inline int qpow(int a,int x){
	int res=1;
	while(x){
		if(x&1) res=(res*a)%MOD;
		a=(a*a)%MOD;
		x>>=1;
	}
	return res%MOD;
}

d e p k dep^k depk现在是权值, d e p dep dep仍线段树最底层去, d e p k dep^k depk来单点改,区间和

但是这题树剖常数大,要T掉一个点

#include
#define in Read()
#define lch p<<1
#define rch p<<1|1
#define int long long
using namespace std;
inline int in{
	int i=0,f=1;char ch;
	while(!isdigit(ch)&&ch!='-')ch=getchar();
	if(ch=='-')ch=getchar(),f=-1;
	while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

const int MOD=998244353;
const int NNN=5e5+10;
int n,m;
vector<int>G[NNN];
int fat[NNN],siz[NNN],top[NNN],dep[NNN],dfn[NNN],son[NNN],tot;
int zOz[NNN];//dfn→dep 
int tre[NNN<<2];//记录原数据 
int val[NNN<<2];//记录幂 
bool exi[NNN<<2];

inline int qpow(int a,int x){
	int res=1;
	while(x){
		if(x&1) res=(res*a)%MOD;
		a=(a*a)%MOD;
		x>>=1;
	}
	return res%MOD;
}

inline void DFS1(int fa,int u){
	fat[u]=fa,siz[u]=1,dep[u]=dep[fa]+1;
	for(int e=0;e<G[u].size();++e){
		int v=G[u][e];
		if(v==fa) continue;
		DFS1(u,v);
		siz[u]+=siz[v];
		if(siz[son[u]]<siz[v]) son[u]=v;
	}
	return;
}

inline void DFS2(int u,int pik){
	top[u]=pik,dfn[u]=++tot,zOz[tot]=dep[u];
	if(!son[u]) return;
	DFS2(son[u],pik);
	for(int e=0;e<G[u].size();++e){
		int v=G[u][e];
		if(v==son[u]||v==fat[u]) continue;
		DFS2(v,v);
	}
	return;
}

inline void build(int p,int l,int r){
	if(l==r){
		tre[p]=zOz[l];
		return;
	}
	int mid=(l+r)>>1;
	build(lch,l,mid);
	build(rch,mid+1,r);
	return;
}

inline int query(int p,int l,int r,int L,int R,int k){
	if(l==r){
		val[p]=qpow(tre[p],k)%MOD;
//		exi[p]=true;printf("%d %d %d %d\n",p,exi[p],tre[p],val[p]);
		return val[p];
	}
	int mid=(l+r)>>1,res=0;
	if(L<=mid) res=(res+query(lch,l,mid,L,R,k))%MOD;
	if(R>mid) res=(res+query(rch,mid+1,r,L,R,k))%MOD;
	return res;
}

inline int sum(int u,int v,int k){
	int res=0;
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]]) swap(u,v);
		res=(res+query(1,1,n,dfn[top[u]],dfn[u],k))%MOD;
		u=fat[top[u]];
	}
	if(dep[u]>dep[v]) swap(u,v);
	res=(res+query(1,1,n,dfn[u],dfn[v],k))%MOD;
	return res;
}

signed main(){
	n=in;
	for(int i=2;i<=n;++i){
		int u=in,v=in;
		G[u].push_back(v);
		G[v].push_back(u);
	}
	DFS1(0,1);
	for(int i=1;i<=n;++i) --dep[i];
	DFS2(1,1);
	build(1,1,n);
	m=in;
	for(int i=1;i<=m;++i){
//		memset(exi,0,sizeof(exi));
		int u=in,v=in,k=in;
		printf("%d\n",sum(u,v,k));
//		for(int i=1;i<=n*4;++i) printf("%d %d %d %d\n",i,exi[i],tre[i],val[i]);puts("");
	}
	return 0;
}

也可以这样优化一下(然鹅并不能过那个T掉的点)

#include
#define in Read()
#define lch p<<1
#define rch p<<1|1
#define int long long
using namespace std;
inline int in{
	int i=0,f=1;char ch;
	while(!isdigit(ch)&&ch!='-')ch=getchar();
	if(ch=='-')ch=getchar(),f=-1;
	while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

const int MOD=998244353;
const int NNN=5e5+10;
int n,m;
vector<int>G[NNN];
int fat[NNN],siz[NNN],top[NNN],dep[NNN],dfn[NNN],son[NNN],tot;
int zOz[NNN];
int tre[NNN<<2][51];

inline int qpow(int a,int x){
	int res=1;
	while(x){
		if(x&1) res=(res*a)%MOD;
		a=(a*a)%MOD;
		x>>=1;
	}
	return res%MOD;
}

inline void DFS1(int fa,int u){
	fat[u]=fa,siz[u]=1,dep[u]=dep[fa]+1;
	for(int e=0;e<G[u].size();++e){
		int v=G[u][e];
		if(v==fa) continue;
		DFS1(u,v);
		siz[u]+=siz[v];
		if(siz[son[u]]<siz[v]) son[u]=v;
	}
	return;
}

inline void DFS2(int u,int pik){
	top[u]=pik,dfn[u]=++tot,zOz[tot]=dep[u];
	if(!son[u]) return;
	DFS2(son[u],pik);
	for(int e=0;e<G[u].size();++e){
		int v=G[u][e];
		if(v==son[u]||v==fat[u]) continue;
		DFS2(v,v);
	}
	return;
}

inline void build(int p,int l,int r){
	if(l==r){
		tre[p][1]=zOz[l];
		return;
	}
	int mid=(l+r)>>1;
	build(lch,l,mid);
	build(rch,mid+1,r);
	return;
}

inline int query(int p,int l,int r,int L,int R,int k){
	if(l==r) return tre[p][k];
	int mid=(l+r)>>1,res=0;
	if(L<=mid) res=(res+query(lch,l,mid,L,R,k))%MOD;
	if(R>mid) res=(res+query(rch,mid+1,r,L,R,k))%MOD;
	return res;
}

inline int sum(int u,int v,int k){
	int res=0;
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]]) swap(u,v);
		res=(res+query(1,1,n,dfn[top[u]],dfn[u],k))%MOD;
		u=fat[top[u]];
	}
	if(dep[u]>dep[v]) swap(u,v);
	res=(res+query(1,1,n,dfn[u],dfn[v],k))%MOD;
	return res;
}

signed main(){
	n=in;
	for(int i=2;i<=n;++i){
		int u=in,v=in;
		G[u].push_back(v);
		G[v].push_back(u);
	}
	DFS1(0,1);
	for(int i=1;i<=n;++i) --dep[i];
	DFS2(1,1);
	build(1,1,n);
	for(int i=1;i<=(n<<2);++i)
		if(tre[i][1])
			for(int j=2;j<=50;++j)
				tre[i][j]=qpow(tre[i][1],j);
	m=in;
	for(int i=1;i<=m;++i){
		int u=in,v=in,k=in;
		printf("%d\n",sum(u,v,k));
	}
	return 0;
}

本解法线段树一定要搜到底,就不优,就很淦,就离谱

T4 Cow Land G

异或显然有性质:
a ⊕ b ⊕ b = a a\oplus b\oplus b=a abb=a

  • 首先,来一波线段树常规操作:读图,两次 DFS,建树
  • 然后,对于查询,查就完了;对于修改,要查到底
    如果要考虑差分的话,码主席树吧

做两道题了,发现了一个常见套路:
建树的时候要求 t r e e tree tree的值,其中 t r e e tree tree线段树节点价值的映射,而我们有的映射只有树上节点编号价值的映射,又发现 t r e e tree tree是自带线段树节点DFS序的映射的,于是考虑复合映射
t r e e : p S G T → v a l t r e e . l : p S G T → d f n u 有 d f n : u → d f n u ,   e : u → e u 建 立 i d x : d f n u → e u tree:p_{SGT}\to val\\ tree.l:p_{SGT}\to dfn_u\\ 有dfn:u\to dfn_u,\ e:u\to e_u\\ 建立idx:dfn_u\to e_u tree:pSGTvaltree.l:pSGTdfnudfn:udfnu, e:ueuidx:dfnueu
我好NaN啊,因此 i d x idx idx就用NaN代替了吧

#include
#define in Read()
#define lch p<<1
#define rch p<<1|1
#define int long long
using namespace std;
inline int in{
	int i=0,f=1;char ch;
	while(!isdigit(ch)&&ch!='-')ch=getchar();
	if(ch=='-')ch=getchar(),f=-1;
	while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

const int NNN=1e6+10;
int n,q,e[NNN];
vector<int>G[NNN];
int fat[NNN],son[NNN],dep[NNN],top[NNN],siz[NNN],dfn[NNN],tot;
int tre[NNN<<2];
int NaN[NNN];//dfn->e 

inline void DFS1(int fa,int u){
	fat[u]=fa,siz[u]=1,dep[u]=dep[fa]+1;
	for(int e=0;e<G[u].size();++e){
		int v=G[u][e];
		if(v==fa) continue;
		DFS1(u,v);
		siz[u]+=siz[v];
		if(siz[son[u]]<siz[v]) son[u]=v;
	}
	return;
}

inline void DFS2(int u,int pik){
	dfn[u]=++tot,top[u]=pik,NaN[tot]=e[u];
	if(!son[u]) return;
	DFS2(son[u],pik);
	for(int e=0;e<G[u].size();++e){
		int v=G[u][e];
		if(v==fat[u]||v==son[u]) continue;
		DFS2(v,v);
	}
	return;
}

inline void push_up(int p){
	tre[p]=tre[lch]^tre[rch];
	return;
}

inline void build(int p,int l,int r){
	if(l==r){
		tre[p]=NaN[l];
		return;
	}
	int mid=(l+r)>>1;
	build(lch,l,mid);
	build(rch,mid+1,r);
	push_up(p);
	return;
}

inline void update(int p,int l,int r,int k,int w){
	if(l==r){
		tre[p]=w;
		return;
	}
	int mid=(l+r)>>1;
	if(k<=mid) update(lch,l,mid,k,w);
	else update(rch,mid+1,r,k,w);
	push_up(p);
	return;
}

inline int query(int p,int l,int r,int L,int R){
	if(L<=l&&r<=R) return tre[p];
	int ans=0,mid=(l+r)>>1;
	if(L<=mid) ans^=query(lch,l,mid,L,R);
	if(R>mid) ans^=query(rch,mid+1,r,L,R);
	return ans;
}

inline void Modify(){
	int u=in,w=in;
	update(1,1,n,dfn[u],w);
	return;
}

inline void Have_dinner(){
	int u=in,v=in,ans=0;
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]]) swap(u,v);
		ans^=query(1,1,n,dfn[top[u]],dfn[u]);
		u=fat[top[u]];
	}
	if(dep[u]>dep[v]) swap(u,v);
	ans^=query(1,1,n,dfn[u],dfn[v]);
	printf("%d\n",ans);
	return;
}

signed main(){
	n=in,q=in;
	for(int i=1;i<=n;++i) e[i]=in;
	for(int i=2;i<=n;++i){
		int u=in,v=in;
		G[u].push_back(v);
		G[v].push_back(u);
	}
	DFS1(0,1);
	DFS2(1,1);
	build(1,1,n);
	for(int i=1;i<=q;++i){
		int opt=in;
		if(opt==1) Modify();
		else Have_dinner();//查询函数,并且表示我饿了 
	}
	return 0;
}

总结经验:int变long,数组开大,AC你拿

T5 Tree

考虑打标记时整个子树中有标记的点之间点所求祖先都是父亲
发现 DFS序 就足够解决了,但是不优,只能算个暴力

省了很多树剖的内容,但是没有删完,按理说只用 DFS序 就够了

#include
#define in Read()
#define lch p<<1
#define rch p<<1|1
#define int long long
using namespace std;
inline int in{
	int i=0,f=1;char ch;
	while(!isdigit(ch)&&ch!='-')ch=getchar();
	if(ch=='-')ch=getchar(),f=-1;
	while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

const int NNN=1e6+10;
int n,q;
vector<int>g[NNN];
int fat[NNN],son[NNN],siz[NNN],dfn[NNN],tot;
int tre[NNN<<2],idx[NNN];

inline void DFS1(int fa,int u){
	fat[u]=fa,siz[u]=1;
	for(int e=0;e<g[u].size();++e){
		int v=g[u][e];
		if(v==fa) continue;
		DFS1(u,v);
		siz[u]+=siz[v];
		if(siz[son[u]]<siz[v]) son[u]=v;
	}
	return;
}

inline void DFS2(int u,int pik){
	dfn[u]=++tot;
	if(!son[u]) return;
	DFS2(son[u],pik);
	for(int e=0;e<g[u].size();++e){
		int v=g[u][e];
		if(v==fat[u]||v==son[u]) continue;
		DFS2(v,v);
	}
	return;
}

inline void build(int p,int l,int r){
	if(l==r){
		tre[p]=1;
		idx[l]=p;
		return;
	}
	int mid=(l+r)>>1;
	build(lch,l,mid);
	build(rch,mid+1,r);
	return;
}

inline void Mark(){
	int u=in;
	int l=dfn[u],r=dfn[u]+siz[u]-1;
	for(int i=l;i<=r;++i){
		tre[idx[i]]=max(tre[idx[i]],u);
	}
	return;
}

inline void Inquire(){
	int u=in;
	printf("%lld\n",tre[idx[dfn[u]]]);
	return;
}

signed main(){
	n=in,q=in;
	for(int i=2;i<=n;++i){
		int u=in,v=in;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	DFS1(0,1);
	DFS2(1,1);
	build(1,1,n);
	for(int i=1;i<=q;++i){
		char opt=getchar();
		while(opt!='C'&&opt!='Q') opt=getchar();
		if(opt=='C') Mark();
		else Inquire();
	}
	return 0;
}

正解:
考虑树剖(别问我怎么想到的,自己看看标题)
在跳重链的时候,如果经过了合法祖先,那么得到答案
考虑 SGT 上维护 区间最深祖先 ,那么搜索的时候搜到了该祖先就可以输出了
至于其它点,SGT 上随便填个 0-1 就行了,不影响

#include
#define in Read()
#define lch p<<1
#define rch p<<1|1
using namespace std;
inline int in{
	int i=0,f=1;char ch;
	while(!isdigit(ch)&&ch!='-')ch=getchar();
	if(ch=='-')ch=getchar(),f=-1;
	while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

const int NNN=1e5+10;
int n,q;
vector<int>G[NNN];
int fat[NNN];
int son[NNN];
int dep[NNN];
int siz[NNN];
int dfn[NNN];//u->dfn
int top[NNN];
int idx[NNN];//dfn->u
int tot;
int tre[NNN<<2];//SGT_p->u

inline void DFS1(int fa,int u){
	fat[u]=fa;
	dep[u]=dep[fa]+1;
	siz[u]=1;
	for(int e=0;e<G[u].size();++e){
		int v=G[u][e];
		if(v==fa) continue;
		DFS1(u,v);
		siz[u]+=siz[v];
		if(siz[son[u]]<siz[v]) son[u]=v;
	}
	return;
}

inline void DFS2(int u,int qwq){
	dfn[u]=++tot;
	idx[tot]=u;
	top[u]=qwq;
	if(!son[u]) return;
	DFS2(son[u],qwq);
	for(int e=0;e<G[u].size();++e){
		int v=G[u][e];
		if(v==fat[u]||v==son[u]) continue;
		DFS2(v,v);
	}
	return;
}

inline void push_up(int p){
	if(dep[tre[p]]<dep[tre[lch]]) tre[p]=tre[lch];
	if(dep[tre[p]]<dep[tre[rch]]) tre[p]=tre[rch];
	return;
}

inline void build(int p,int l,int r){
	if(l==r){
		tre[p]=0;
		return;
	}
	int mid=(l+r)>>1;
	build(lch,l,mid);
	build(rch,mid+1,r);
	push_up(p);
	return;
}

inline void update(int p,int l,int r,int k,int w){
	if(l==r){
		tre[p]=w;
		return;
	}
	int mid=(l+r)>>1;
	if(k<=mid) update(lch,l,mid,k,w);
	else update(rch,mid+1,r,k,w);
	push_up(p);
	return;
}

inline int query(int p,int l,int r,int L,int R){
	if(L<=l&&r<=R) return tre[p];
	int mid=(l+r)>>1,tmp,res=0;
	if(L<=mid){
		tmp=query(lch,l,mid,L,R);
		if(dep[tmp]>dep[res]) res=tmp;
	}
	if(R>mid){
		tmp=query(rch,mid+1,r,L,R);
		if(dep[tmp]>dep[res]) res=tmp;
	}
	return res;
}

inline void buy_kvass(int u){
	update(1,1,n,dfn[u],u);
	return;
}

inline int kvass(int u){
	int v=1,ans;
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]]) swap(u,v);
		ans=query(1,1,n,dfn[top[u]],dfn[u]);
		if(ans) return ans;
		u=fat[top[u]];
	}
	if(dep[u]>dep[v]) swap(u,v);
	ans=query(1,1,n,dfn[u],dfn[v]);
	return ans?ans:1;
}

inline void drink_kvass(int u){
	printf("%d\n",kvass(u));
	return;
}

int main(){
	n=in,q=in;
	for(int i=2;i<=n;++i){
		int u=in,v=in;
		G[u].push_back(v);
		G[v].push_back(u);
	}
	DFS1(0,1);
	DFS2(1,1);
	build(1,1,n);
	for(int i=1;i<=q;++i){
		char opt=getchar();
		while(opt!='Q'&&opt!='C') opt=getchar();
		if(opt=='C') buy_kvass(in);//put a tag on the dot, or update the deepest dot
		else drink_kvass(in);//get the deepest legal ancestor
	}
	return 0;
}

心情不好
变量名有点奇怪,抱歉
不过,

洛谷里那个人的代码是真的丑!!!!!!

T6 Travel

考试题,考场上树剖4K码炸了,整出来了一个只能正确求出从深度大的点走到深度小的点的代码

考虑维护几个值:(树剖你维护就是了,主要是要想清楚要维护什么
区间 max,区间 min,两个方向的利润最大值( forward , bakward ),当然还需要 lazy_tag

其中 f o r w a r d p = max ⁡ { f o r w a r d l c h , f o r w a r d r c h , m a x x l c h − m i n n r c h } b a k w a r d p = max ⁡ { b a k w a r d l c h , b a k w a r d r c h , m a x x r c h − m i n n l c h } forward_p=\max\{ forward_{lch}, forward_{rch},maxx_{lch}-minn_{rch}\}\\bakward_p=\max\{ bakward_{lch}, bakward_{rch},maxx_{rch}-minn_{lch}\} forwardp=max{forwardlch,forwardrch,maxxlchminnrch}bakwardp=max{bakwardlch,bakwardrch,maxxrchminnlch}

变量名取长了就丑

调了一个晚上
(不过机房人谁不是这样的呢)
感谢 gigo 巨佬,Our best wishes are given him and we pray him to AK IOI
最后发现是我用了C++11,洛谷炸了,洛谷不能用C++11

洛谷傻逼
#include
#define in Read()
#define int long long
using namespace std;
inline int in{
	int i=0,f=1;char ch;
	while(!isdigit(ch)&&ch!='-')ch=getchar();
	if(ch=='-')ch=getchar(),f=-1;
	while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

const int NNN=1e5+10;
const int INF=1e9+10;
int n,q;
int tot;
vector<int>G[NNN];
int wei[NNN];//u->value
int faz[NNN];
int son[NNN];
int dep[NNN];
int dfn[NNN];
int idx[NNN];//inverse map of dfn
int ord;//dfs order
int siz[NNN];
int top[NNN];
int sgn[NNN];//to mark whether to take forward or backward
struct SGT{
	int l,r;
	int max_;
	int min_;
	int lmax;//go from down to up, forward
	int rmax;//go from up to down, backward
	int lazy;
	
	inline void Clear(){
		max_=0;
		min_=INF;
		lmax=0;
		rmax=0;
		return;
	}
	
}tre[NNN<<2];

inline void DFS1(int fa,int u){
	faz[u]=fa;
	dep[u]=dep[fa]+1;
	siz[u]=1;
	for(int e=0;e<G[u].size();++e){
		int v=G[u][e];
		if(v==fa) continue;
		DFS1(u,v);
		siz[u]+=siz[v];
		if(siz[son[u]]<siz[v]) son[u]=v;
	}
	return;
}

inline void DFS2(int u,int qwq){
	dfn[u]=++ord;
	top[u]=qwq;
	idx[ord]=u;
	if(!son[u]) return;
	DFS2(son[u],qwq);
	for(int e=0;e<G[u].size();e++){
		int v=G[u][e];
		if(v==faz[u]||v==son[u]) continue;
		DFS2(v,v);
	}
	return;
}

#define lch p<<1
#define rch p<<1|1
#define tl tre[p].l
#define tr tre[p].r

inline void push_up(int p){
	tre[p].max_=max(tre[lch].max_,tre[rch].max_);
	tre[p].min_=min(tre[lch].min_,tre[rch].min_);
	tre[p].lmax=max(tre[lch].max_-tre[rch].min_,max(tre[lch].lmax,tre[rch].lmax));
	tre[p].rmax=max(tre[rch].max_-tre[lch].min_,max(tre[lch].rmax,tre[rch].rmax));
	return;
}

inline void push_down(int p){
	if(!tre[p].lazy) return;
	tre[lch].max_+=tre[p].lazy;
	tre[rch].max_+=tre[p].lazy;
	tre[lch].min_+=tre[p].lazy;
	tre[rch].min_+=tre[p].lazy;
	tre[lch].lazy+=tre[p].lazy;
	tre[rch].lazy+=tre[p].lazy;
	tre[p].lazy=0;
	return;
}

inline SGT merge(SGT l,SGT r){
	SGT res;
	res.max_=max(l.max_,r.max_);
	res.min_=min(l.min_,r.min_);
	res.lmax=max(l.max_-r.min_,max(l.lmax,r.lmax));
	res.rmax=max(r.max_-l.min_,max(l.rmax,r.rmax));
	return res;
}

inline void build(int p,int l,int r){
	tl=l;tr=r;
	if(l==r){
		tre[p].max_=wei[idx[l]];
		tre[p].min_=wei[idx[l]];
		return;
	}
	int mid=l+r>>1;
	build(lch,l,mid);
	build(rch,mid+1,r);
	push_up(p);
	return;
}

inline SGT query(int p,int l,int r){
	if(l<=tl&&tr<=r)
		return tre[p];
	push_down(p);
	int mid=tl+tr>>1;
	if(r<=mid) return query(lch,l,r);
	if(l>mid) return query(rch,l,r);
	return merge(query(lch,l,r),query(rch,l,r));
}

inline void update(int p,int l,int r,int w){
	if(l<=tl&&tr<=r){
		tre[p].max_+=w;
		tre[p].min_+=w;
		tre[p].lazy+=w;
		return;
	}
	push_down(p);
	int mid=tr+tl>>1;
	if(l<=mid) update(lch,l,r,w);
	if(r>mid) update(rch,l,r,w);
	push_up(p);
	return;
}

#undef lch
#undef rch
#undef tl
#undef tr

inline int Sum(int u,int v){
	SGT l,r;
	l.Clear();r.Clear();
	l.min_=INF;
	r.min_=INF;
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]]){
			r=merge(query(1,dfn[top[v]],dfn[v]),r);
			v=faz[top[v]];
		}else{
			l=merge(query(1,dfn[top[u]],dfn[u]),l);
			u=faz[top[u]];
		}
	}
	if(dep[u]>dep[v]) l=merge(query(1,dfn[v],dfn[u]),l);
	else r=merge(query(1,dfn[u],dfn[v]),r);
	swap(l.lmax,l.rmax);
	return merge(l,r).rmax;
}

inline void Modify(int u,int v,int w){
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]]) swap(u,v);
		update(1,dfn[top[u]],dfn[u],w);
		u=faz[top[u]];
	}
	if(dep[u]>dep[v]) swap(u,v);
	update(1,dfn[u],dfn[v],w);
	return;
}

signed main(){
//	freopen("my.out","w",stdout);
	n=in;
	for(int i=1;i<=n;++i) wei[i]=in;
	for(int i=2;i<=n;++i){
		int u=in,v=in;
		G[u].push_back(v);
		G[v].push_back(u);
	}
	DFS1(0,1);
	DFS2(1,1);
	build(1,1,n);
	q=in;
	for(int i=1;i<=q;++i){
		int u=in,v=in,w=in;
		printf("%lld\n",Sum(u,v));
		Modify(u,v,w);
	}
	return 0;
}

T7 月下“毛景树”

此题不板,何板之有?
是不板,孰板?

T1,需要边权下放到点权,考虑 DFS1 的时候就下放
注意 LCA 不能算进去

注意要先cover

gigo 大佬是算了 LCA ,调了一下午,我是没考虑 coveradd 操作的优先级,调了一下午
coveradd 要优先一些,因为 coveradd 就没用了

单独下放边权,思路比较清晰一些(giaohr巨巨的代码;并且我请求大家不要访问他的博客,因为这样会增加他的访问量)

#include
#define in Read()
#define int long long
using namespace std;
inline int in{
	int i=0,f=1;char ch;
	while(!isdigit(ch)&&ch!='-')ch=getchar();
	if(ch=='-')ch=getchar(),f=-1;
	while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

const int NNN=1e5+5;
const int INF=1e9+7;
int n;
char q[50];
int tot;
int first[NNN];
int nxt[NNN<<1];
int aim[NNN<<1];
int wei[NNN];
int to1[NNN];
int to2[NNN];
int faz[NNN];
int dep[NNN];
int siz[NNN];
int son[NNN];
int top[NNN];
int dfn[NNN];
int idx[NNN];
int dwn[NNN];
int val[NNN];
int ord;
struct Tree{
	int l,r;
	int max;
	int add;
	int cov;
}tre[NNN<<2];

inline void add(int u,int v){
	++tot;
	nxt[tot]=first[u];
	first[u]=tot;
	aim[tot]=v;
	return;
}

inline void DFS1(int fa,int u){
	faz[u]=fa;
	dep[u]=dep[fa]+1;
	siz[u]=1;
	for(int e=first[u];e;e=nxt[e]){
		int v=aim[e];
		if(v==fa) continue;
		DFS1(u,v);
		siz[u]+=siz[v];
		if(siz[son[u]]<siz[v]) son[u]=v;
	}
	return;
}

inline void DFS2(int u,int tp){
	top[u]=tp;
	dfn[u]=++ord;
	idx[ord]=u;
	if(!son[u]) return;
	DFS2(son[u],tp);
	for(int e=first[u];e;e=nxt[e]){
		int v=aim[e];
		if(v==faz[u]||v==son[u]) continue;
		DFS2(v,v);
	}
	return;
}

inline void push_road(){
	for(int i=1;i<n;++i){
		dwn[i]=dep[to1[i]]<dep[to2[i]]?to2[i]:to1[i];
		val[dwn[i]]=wei[i];
	}
	return;
}

#define lch p<<1
#define rch p<<1|1
#define tr tre[p].r
#define tl tre[p].l

inline void push_up(int p){
	tre[p].max=max(tre[lch].max,tre[rch].max);
	return ;
}

inline void push_down(int p){
	if(tre[p].cov!=-1){
		tre[lch].max=tre[p].cov;
		tre[rch].max=tre[p].cov;
		tre[lch].cov=tre[p].cov;
		tre[rch].cov=tre[p].cov;
		tre[lch].add=0;
		tre[rch].add=0;
		tre[p].cov=-1;
	}
	if(tre[p].add){
		tre[lch].add+=tre[p].add;
		tre[rch].add+=tre[p].add;
		tre[lch].max+=tre[p].add;
		tre[rch].max+=tre[p].add;
		tre[p].add=0;
	}
	return;
}

inline void build(int p,int l,int r){
	tr=r,tl=l;
	tre[p].cov=-1;
	tre[p].max=-INF;
	if(l==r){
		tre[p].max=val[idx[l]];
		return;
	}
	int mid=l+r>>1;
	build(lch,l,mid);
	build(rch,mid+1,r);
	push_up(p);
	return;
}

inline void cover(int p,int l,int r,int w){
	if(l<=tl&&tr<=r){
		tre[p].max=w;
		tre[p].cov=w;
		tre[p].add=0;
		return;
	}
	push_down(p);
	int mid=tl+tr>>1;
	if(l<=mid) cover(lch,l,r,w);
	if(r>mid) cover(rch,l,r,w);
	push_up(p);
	return;
}

inline void update(int p,int l,int r,int w){
	if(l<=tl&&tr<=r){
		tre[p].max+=w;
		tre[p].add+=w;
		return;
	}
	push_down(p);
	int mid=tr+tl>>1;
	if(l<=mid) update(lch,l,r,w);
	if(r>mid) update(rch,l,r,w);
	push_up(p);
	return;
}

inline int query(int p,int l,int r){
	if(l<=tl&&tr<=r) return tre[p].max;
	int ans=0;
	push_down(p);
	int mid=tr+tl>>1;
	if(l<=mid) ans=max(ans,query(lch,l,r));
	if(r>mid) ans=max(ans,query(rch,l,r));
	return ans;
}

#undef lch
#undef rch
#undef tr
#undef tl

inline void Cover(int u,int v,int w){
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]]) swap(u,v);
		cover(1,dfn[top[u]],dfn[u],w);
		u=faz[top[u]];
	}
	if(u==v) return;
	if(dep[u]>dep[v]) swap(u,v);
	cover(1,dfn[u]+1,dfn[v],w);
	return;
}

inline void Add(int u,int v,int w){
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]]) swap(u,v);
		update(1,dfn[top[u]],dfn[u],w);
		u=faz[top[u]];
	}
	if(u==v) return;
	if(dep[u]>dep[v]) swap(u,v);
	update(1,dfn[u]+1,dfn[v],w);
	return;
}

inline int Query(int u,int v){
	int ans=-INF;
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]]) swap(u,v);
		ans=max(ans,query(1,dfn[top[u]],dfn[u]));
		u=faz[top[u]];
	}
	if(u==v) return ans;
	if(dep[u]>dep[v]) swap(u,v);
	ans=max(ans,query(1,dfn[u]+1,dfn[v]));
	return ans;	
}

signed main(){
	
//	freopen("moon.in","r",stdin);
//	freopen("moon.out","w",stdout);
	
	n=in;
	for(int i=1;i<n;++i){
		int u=in,v=in,w=in;
		add(u,v);
		add(v,u);
		to1[i]=u;
		to2[i]=v;
		wei[i]=w;
	}
	DFS1(0,1);
	DFS2(1,1);
	push_road();
	build(1,1,n);
	scanf("%s",q);
	while(q[0]!='S'){
		if(q[1]=='h'){
			int k=in,w=in;
			cover(1,dfn[dwn[k]],dfn[dwn[k]],w);
		}else if(q[1]=='o'){
			int u=in,v=in,w=in;
			Cover(u,v,w);
		}else if(q[1]=='d'){
			int u=in,v=in,w=in;
			Add(u,v,w);
		}else if(q[1]=='a'){
			int u=in,v=in;
			printf("%lld\n",Query(u,v));
		}scanf("%s",q);
	}
	return 0;
}

另外的DFS1就下放的代码,我先咕了~

T8 Magical Tree

这题有毒,周末想做,每次看到 Harry邓教 就毅然打开 B站 开始 三刷
然后一个周末就这样荒废了

不过我们终于迎来了一道 板子题
0 开始的点直接搞成从 1 开始,常规操作

#include
#define in Read()
#define int long long
using namespace std;
inline int in{
	int i=0,f=1;char ch;
	while(!isdigit(ch)&&ch!='-')ch=getchar();
	if(ch=='-')ch=getchar(),f=-1;
	while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

const int NNN=1e5+10;
int n,q;
int first[NNN];
int nxt[NNN<<1];
int aim[NNN<<1];
int tot;
int faz[NNN];
int son[NNN];
int top[NNN];
int dep[NNN];
int dfn[NNN];
int idx[NNN];
int ord;
int siz[NNN];
struct Tree{
	int l,r;
	int sum;
	int laz;
}tre[NNN<<2];

inline void add(int u,int v){
	++tot;
	nxt[tot]=first[u];
	first[u]=tot;
	aim[tot]=v;
}

inline void DFS1(int fa,int u){
	faz[u]=fa;
	dep[u]=dep[fa]+1;
	siz[u]=1;
	for(int e=first[u];e;e=nxt[e]){
		int v=aim[e];
		if(v==fa) continue;
		DFS1(u,v);
		siz[u]+=siz[v];
		if(siz[son[u]]<siz[v]) son[u]=v;
	}
	return;
}

inline void DFS2(int u,int tp){
	top[u]=tp;
	dfn[u]=++ord;
	idx[ord]=u;
	if(!son[u]) return;
	DFS2(son[u],tp);
	for(int e=first[u];e;e=nxt[e]){
		int v=aim[e];
		if(v==son[u]||v==faz[u]) continue;
		DFS2(v,v);
	}
	return;
}

#define lch p<<1
#define rch p<<1|1
#define tl tre[p].l
#define tr tre[p].r

inline void push_up(int p){
	tre[p].sum=tre[lch].sum+tre[rch].sum;
	return;
}

inline void push_down(int p,int len){
	if(!tre[p].laz) return;
	tre[lch].sum+=tre[p].laz*(len-(len>>1));
	tre[rch].sum+=tre[p].laz*(len>>1);
	tre[lch].laz+=tre[p].laz;
	tre[rch].laz+=tre[p].laz;
	tre[p].laz=0;
	return;
}

inline void build(int p,int l,int r){
	tl=l;
	tr=r;
	if(l==r) return;
	int mid=l+r>>1;
	build(lch,l,mid);
	build(rch,mid+1,r);
	return;
}

inline void update(int p,int l,int r,int w){
	if(l<=tl&&tr<=r){
		tre[p].sum+=w*(tr-tl+1);
		tre[p].laz+=w;
		return;
	}
	push_down(p,tr-tl+1);
	int mid=tr+tl>>1;
	if(l<=mid) update(lch,l,r,w);
	if(r>mid) update(rch,l,r,w);
	push_up(p);
	return;
}

inline int query(int p,int l,int r){
	if(l<=tl&&tr<=r) return tre[p].sum;
	push_down(p,tr-tl+1);
	int mid=tl+tr>>1,res=0;
	if(l<=mid) res+=query(lch,l,r);
	if(r>mid) res+=query(rch,l,r);
	return res;
}

#undef lch
#undef rch
#undef tl
#undef tr

inline void Modify(int u,int v,int w){
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]]) swap(u,v);
		update(1,dfn[top[u]],dfn[u],w);
		u=faz[top[u]];
	}
	if(dep[u]>dep[v]) swap(u,v);
	update(1,dfn[u],dfn[v],w);
	return;
}

signed main(){
	n=in;
	for(int i=1;i<n;++i){
		int u=in,v=in;
		++u,++v;
		add(u,v);
		add(v,u);
	}
	DFS1(0,1);
	DFS2(1,1);
	build(1,1,n);
	q=in;
	for(int i=1;i<=n;++i){
		char s=getchar();
		while(s!='A'&&s!='Q') s=getchar();
		if(s=='A'){
			int u=in,v=in,w=in;
			++u,++v;
			Modify(u,v,w);
		}else if(s=='Q'){
			int u=in;
			++u;
			printf("%lld\n",query(1,dfn[u],dfn[u]+siz[u]-1));
		}
	}
	return 0;
}

T9 软件包管理器

第一眼:拓扑排序
发现是看走眼了

install 节点到根搜一遍为 0 的,然后全部加上 1
uninstall 子树根到子树所有节点搜一遍为 1 的,然后全部变为 0

注意从某个点到顶点必须要树剖一下,因为跨越重链的 DFS序 不连续

#include
#define in Read()
using namespace std;
inline int in{
	int i=0,f=1;char ch;
	while(!isdigit(ch)&&ch!='-')ch=getchar();
	if(ch=='-')ch=getchar(),f=-1;
	while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

const int N=1e5+5;
int n,q;
char s[50];
int first[N];
int nxt[N<<1];
int aim[N<<1];
int faz[N];
int son[N];
int dep[N];
int top[N];
int dfn[N];
int siz[N];
int tot;
int ord;
struct Tree{
	int l,r;
	int sum;
	int laz;
}tre[N<<2];

inline void add(int u,int v){
	++tot;
	nxt[tot]=first[u];
	first[u]=tot;
	aim[tot]=v;
	return;
}

inline void DFS1(int fa,int u){
	faz[u]=fa;
	dep[u]=dep[fa]+1;
	siz[u]=1;
	for(int e=first[u];e;e=nxt[e]){
		int v=aim[e];
		if(v==fa) continue;
		DFS1(u,v);
		siz[u]+=siz[v];
		if(siz[son[u]]<siz[v]) son[u]=v;
	}
	return;
}

inline void DFS2(int u,int tp){
	top[u]=tp;
	dfn[u]=++ord;
	if(!son[u]) return;
	DFS2(son[u],tp);
	for(int e=first[u];e;e=nxt[e]){
		int v=aim[e];
		if(v==faz[u]||v==son[u]) continue;
		DFS2(v,v);
	}
	return;
}

#define lch p<<1
#define rch p<<1|1
#define tr tre[p].r
#define tl tre[p].l

inline void push_up(int p){
	tre[p].sum=tre[lch].sum+tre[rch].sum;
	return;
}

inline void push_down(int p){
	if(tre[p].laz==-1) return;
	if(tre[p].laz==1){
		tre[lch].laz=tre[p].laz;
		tre[rch].laz=tre[p].laz;
		tre[lch].sum=tre[lch].r-tre[lch].l+1;
		tre[rch].sum=tre[rch].r-tre[rch].l+1;
		tre[p].laz=-1;
	}
	if(tre[p].laz==0){
		tre[lch].laz=tre[p].laz;
		tre[rch].laz=tre[p].laz;
		tre[lch].sum=0;
		tre[rch].sum=0;
		tre[p].laz=-1;
	}
	return;
}

inline void build(int p,int l,int r){
	tl=l,tr=r;
	tre[p].sum=0;
	tre[p].laz=-1;
	if(l==r) return;
	int mid=l+r>>1;
	build(lch,l,mid);
	build(rch,mid+1,r);
	return;
}

inline void update(int p,int l,int r,int w){
	if(l<=tl&&tr<=r){
		tre[p].sum=(tr-tl+1)*w;
		tre[p].laz=w;
		return;
	}
	push_down(p);
	int mid=tr+tl>>1;
	if(l<=mid) update(lch,l,r,w);
	if(r>mid) update(rch,l,r,w);
	push_up(p);
	return;
}

inline int query(int p,int l,int r){
	if(l<=tl&&tr<=r) return tre[p].sum;
	int mid=tr+tl>>1,res=0;
	push_down(p);
	if(l<=mid) res+=query(lch,l,r);
	if(r>mid) res+=query(rch,l,r);
	push_up(p);
	return res;
}

#undef lch
#undef rch
#undef tr
#undef tl

inline void Modify(int u,int v,int w){
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]]) swap(u,v);
		update(1,dfn[top[u]],dfn[u],w);
		u=faz[top[u]];
	}
	if(dep[u]>dep[v]) swap(u,v);
	update(1,dfn[u],dfn[v],w);
	return;
}

int main(){
	n=in;
	for(int i=1;i<n;++i){
		int u=in;
		add(u+1,i+1);
		add(i+1,u+1);
	}
	
	DFS1(0,1);
	DFS2(1,1);
	build(1,1,n);
	q=in;
	for(int i=1;i<=q;++i){
		scanf("%s",s);
		int t1=tre[1].sum;
		if(s[0]=='i'){
			int x=in+1;
			Modify(1,x,1);
			printf("%d\n",abs(t1-tre[1].sum));
		}else if(s[0]=='u'){
			int x=in+1;
			update(1,dfn[x],dfn[x]+siz[x]-1,0);
			printf("%d\n",abs(t1-tre[1].sum));
		}
	}
	return 0;
}

树剖太NaN了,我刚多项式去了,暑假再回来

你可能感兴趣的:(#,树剖)