zoukankan      html  css  js  c++  java
  • 虚树总结&题单&简要题解

    简介

    虚树,即剔除所有无关结点,只保留询问点和询问点的相关结点(两两之间的LCA),建一棵新树,这棵新树就是虚树。通过虚树,可以有效的减小询问(甚至修改)的复杂度。设询问点的个数是(k),那么建虚树的一般方法的时间复杂度为(O(k log k))

    构建方法

    1. 把所有询问点按dfs序排个序。

    2. 求出所有相邻结点的LCA(相关点)加入数组,结束后把根结点((1))也加入数组。

    3. 再把所有询问点和相关点按dfs序排个序。

    4. 用栈维护虚树上根结点出发的一条链,按dfs序逐个插入结点,弹栈时连虚树边。

    5. 逐个弹出栈中剩余结点,并同时连虚树边。

    代码

    void build_itree(){
    	rin(i,1,cnt) arr[++len]=uu[i],arr[++len]=vv[i];
    	std::sort(arr+1,arr+len+1,cmp);
    	len=std::unique(arr+1,arr+len+1)-arr-1;
    	int temp=len;
    	rin(i,1,temp-1) arr[++len]=lca(arr[i],arr[i+1]);
    	arr[++len]=1;
    	std::sort(arr+1,arr+len+1,cmp);
    	len=std::unique(arr+1,arr+len+1)-arr-1;
    	rin(i,1,len) id2[arr[i]]=i;
    	top=0;
    	rin(i,1,len){
    		ini[arr[i]]=true;
    		while(top&&id[sta[top]]+siz[sta[top]]-1<id[arr[i]]) add_iedge(id2[sta[top-1]],id2[sta[top]]),top--;
    		sta[++top]=arr[i];
    	}
    	while(top>1) add_iedge(id2[sta[top-1]],id2[sta[top]]),top--;
    }
    

    [BZOJ2286][SDOI2011]消耗战

    分析

    模板题。对于每个询问,建出虚树后在虚树上跑树形DP(当时并不会建虚树所以直接抄的题解,建虚树的方法可能和上面讲的不太一样)即可。

    代码

    #include <bits/stdc++.h>
    #define rin(i,a,b) for(register int i=(a);i<=(b);++i)
    #define irin(i,a,b) for(register int i=(a);i>=(b);--i)
    #define trav(i,a) for(register int i=head[a];i;i=e[i].nxt)
    typedef long long LL;
    using std::cin;
    using std::cout;
    using std::endl;
    
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    }
    
    const int MAXN=250005;
    int n,m,k,ecnt,head[MAXN];
    int fa[MAXN],dep[MAXN],siz[MAXN],pc[MAXN],_top[MAXN],id[MAXN],tot;
    int h[MAXN<<1],sta[MAXN],top;
    LL f[MAXN],minw[MAXN];
    std::vector<int> vec[MAXN];
    struct Edge{
    	int to,nxt,w;
    }e[MAXN<<1];
    
    inline void add_edge(int bg,int ed,int val){
    	++ecnt;
    	e[ecnt].nxt=head[bg];
    	e[ecnt].to=ed;
    	e[ecnt].w=val;
    	head[bg]=ecnt;
    }
    
    void dfs1(int x,int pre,int depth){
    	fa[x]=pre;
    	dep[x]=depth;
    	siz[x]=1;
    	int maxsiz=-1;
    	trav(i,x){
    		int ver=e[i].to;
    		if(ver==pre) continue;
    		minw[ver]=std::min(minw[x],1ll*e[i].w);
    		dfs1(ver,x,depth+1);
    		siz[x]+=siz[ver];
    		if(siz[ver]>maxsiz){
    			maxsiz=siz[ver];
    			pc[x]=ver;
    		}
    	}
    }
    
    void dfs2(int x,int topf){
    	_top[x]=topf;
    	id[x]=++tot;
    	if(!pc[x]) return;
    	dfs2(pc[x],topf);
    	trav(i,x){
    		int ver=e[i].to;
    		if(ver==fa[x]||ver==pc[x]) continue;
    		dfs2(ver,ver);
    	}
    }
    
    inline int lca(int x,int y){
    	while(_top[x]!=_top[y]){
    		if(dep[_top[x]]<dep[_top[y]]) std::swap(x,y);
    		x=fa[_top[x]];
    	}
    	return dep[x]<dep[y]?x:y;
    }
    
    inline bool cmp(int x,int y){
    	return id[x]<id[y];
    }
    
    inline void add_iedge(int bg,int ed){
    	vec[bg].push_back(ed);
    }
    
    void ins(int x){
    	if(top==1){
    		sta[++top]=x;
    		return;
    	}
    	int _lca=lca(x,sta[top]);
    	if(_lca==sta[top]) return;
    	while(top>1&&id[sta[top-1]]>=id[_lca]) add_iedge(sta[top-1],sta[top]),top--;
    	if(_lca!=sta[top]) add_iedge(_lca,sta[top]),sta[top]=_lca;
    	sta[++top]=x;
    }
    
    void dfs3(int x){
    	if(!vec[x].size()){
    		f[x]=minw[x];
    		return;
    	}
    	f[x]=0;
    	rin(i,0,(int)vec[x].size()-1){
    		int ver=vec[x][i];
    		dfs3(ver);
    		f[x]+=std::min(f[ver],minw[ver]);
    	}
    	vec[x].clear();
    }
    
    int main(){
    	n=read();
    	rin(i,2,n){
    		int u=read(),v=read(),w=read();
    		add_edge(u,v,w);
    		add_edge(v,u,w);
    	}
    	minw[1]=1e18;
    	dfs1(1,0,1);
    	dfs2(1,1);
    	m=read();
    	while(m--){
    		k=read();
    		rin(i,1,k) h[i]=read();
    		std::sort(h+1,h+k+1,cmp);
    		top=1,sta[1]=1;
    		rin(i,1,k) ins(h[i]);
    		while(top>1) add_iedge(sta[top-1],sta[top]),top--;
    		dfs3(1);
    		printf("%lld
    ",f[1]);
    	}
    	return 0;
    }
    

    [CF1073G]Yet Another LCP Problem

    分析

    说白了就是给SAM的parent树建虚树。

    众所周知,parent树是反串的后缀树。将字符串reverse(),建出后缀树。于是我们所求的问题转化为了给你两个点集,要求所有两个结点分别来自不同点集的点对的LCA的深度之和。

    这个问题似乎可以用[BZOJ3626][LNOI2014]LCA的方法解决,不过我们还可以做到更快。考虑虚树上每条边的贡献,显然(?)是(cnta[x] imes cntb[x] imes w[x]),即子树内集合(A)的点的个数乘集合(B)的点的个数再乘这条边的边权(后缀树上这个转移的串长)。

    代码

    #include <bits/stdc++.h>
    #define rin(i,a,b) for(register int i=(a);i<=(b);++i)
    #define irin(i,a,b) for(register int i=(a);i>=(b);--i)
    #define trav(i,a) for(register int i=head[a];i;i=e[i].nxt)
    typedef long long LL;
    using std::cin;
    using std::cout;
    using std::endl;
    
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    }
    
    const int MAXN=200005;
    int n,q,las,tot,ecnt,head[MAXN<<1];
    int fa[MAXN<<1],dep[MAXN<<1],siz[MAXN<<1],pc[MAXN<<1],_top[MAXN<<1],id[MAXN<<1],totid;
    int seta[MAXN<<1],setb[MAXN<<1],siza,sizb,arr[MAXN<<2],len,sta[MAXN<<1],top;
    int frm[MAXN<<1],cnta[MAXN<<1],cntb[MAXN<<1];
    char s[MAXN];
    std::vector<int> vec[MAXN<<1];
    struct sam{
    	int fa,to[26];
    	int len;
    }a[MAXN<<1];
    struct Edge{
    	int to,nxt;
    }e[MAXN<<1];
    
    inline void add_edge(int bg,int ed){
    	ecnt++;
    	e[ecnt].to=ed;
    	e[ecnt].nxt=head[bg];
    	head[bg]=ecnt;
    }
    
    inline void add_iedge(int bg,int ed){
    	vec[bg].push_back(ed);
    }
    
    void extend(int c,int np){
    	int p=las;las=np,a[np].len=a[p].len+1;
    	while(p&&!a[p].to[c]) a[p].to[c]=np,p=a[p].fa;
    	if(!p){a[np].fa=1;return;}
    	int q=a[p].to[c];
    	if(a[p].len+1==a[q].len){a[np].fa=q;return;}
    	int nq=++tot;a[nq]=a[q],a[nq].len=a[p].len+1,a[np].fa=a[q].fa=nq;
    	while(p&&a[p].to[c]==q) a[p].to[c]=nq,p=a[p].fa;
    }
    
    void dfs1(int x,int pre,int depth){
    	fa[x]=pre;
    	dep[x]=depth;
    	siz[x]=1;
    	int maxsiz=-1;
    	trav(i,x){
    		int ver=e[i].to;
    		dfs1(ver,x,depth+1);
    		siz[x]+=siz[ver];
    		if(siz[ver]>maxsiz){
    			maxsiz=siz[ver];
    			pc[x]=ver;
    		}
    	}
    }
    
    void dfs2(int x,int topf){
    	_top[x]=topf;
    	id[x]=++totid;
    	if(!pc[x]) return;
    	dfs2(pc[x],topf);
    	trav(i,x){
    		int ver=e[i].to;
    		if(ver==pc[x]) continue;
    		dfs2(ver,ver);
    	}
    }
    
    inline int lca(int x,int y){
    	while(_top[x]!=_top[y]){
    		if(dep[_top[x]]<dep[_top[y]]) std::swap(x,y);
    		x=fa[_top[x]];
    	}
    	return dep[x]<dep[y]?x:y;
    }
    
    inline bool cmp(int x,int y){
    	return id[x]<id[y];
    }
    
    int main(){
    	n=read(),q=read(),las=1,tot=n+1;
    	scanf("%s",s+1);
    	irin(i,n,1) extend(s[i]-'a',i+1);
    	rin(i,2,tot) add_edge(a[i].fa,i);
    	dfs1(1,0,1);
    	dfs2(1,1);
    	while(q--){
    		LL ans=0;
    		siza=read(),sizb=read(),len=0;
    		rin(i,1,siza) arr[++len]=seta[i]=read()+1,cnta[seta[i]]=1;
    		rin(i,1,sizb) arr[++len]=setb[i]=read()+1,cntb[setb[i]]=1;
    		std::sort(arr+1,arr+len+1,cmp);
    		len=std::unique(arr+1,arr+len+1)-arr-1;
    		int temp=len;
    		rin(i,1,temp-1) arr[++len]=lca(arr[i],arr[i+1]);
    		arr[++len]=1;
    		std::sort(arr+1,arr+len+1,cmp);
    		len=std::unique(arr+1,arr+len+1)-arr-1;
    		top=0;
    		rin(i,1,len){
    			while(top&&id[sta[top]]+siz[sta[top]]-1<id[arr[i]]) add_iedge(sta[top-1],sta[top]),frm[sta[top]]=a[sta[top]].len-a[sta[top-1]].len,top--;
    			sta[++top]=arr[i];
    		}
    		rin(i,1,top-1) add_iedge(sta[i],sta[i+1]),frm[sta[i+1]]=a[sta[i+1]].len-a[sta[i]].len;
    		irin(i,len,1){
    			int x=arr[i];
    			rin(j,0,(int)vec[x].size()-1){
    				int ver=vec[x][j];
    				cnta[x]+=cnta[ver];
    				cntb[x]+=cntb[ver];
    			}
    			ans+=1ll*cnta[x]*cntb[x]*frm[x];
    		}
    		rin(i,1,len) cnta[arr[i]]=cntb[arr[i]]=0,vec[arr[i]].clear();
    		printf("%I64d
    ",ans);
    	}
    	return 0;
    }
    

    [BZOJ5287][HNOI2018]毒瘤

    分析

    这题是真毒瘤,敲了2h+。

    观察数据范围发现边数并不比点数多很多,建出一棵生成树后,最多只会余下(11)条边。所以可以先随便求出原图的一棵生成树,把多出来的边的相关结点揪出来搞一棵虚树,在虚树上的转移提前算好系数(k[x][0/1][0/1]),表示结点(x)选或不选对其虚树上的父亲选或不选的贡献的系数,最后枚举每条多出来的边的两端点的选取情况(可以通过强制其中一个端点选或不选来实现),做树形DP即可。

    举例,考虑如何求(k[x][0/1][0])(即不选结点(x)的贡献),容易想到虚树上的一条边对应原树上的一条链。令(f[x][0]=1,f[x][1]=0),这样可以确保算的只是不选结点(x)的贡献,单独把这条链拿出来做树形DP就好了,记得过程中算上分枝的贡献。求(k[x][0/1][1])的过程类似。

    实现复杂,代码超长,细节超多,调试困难,为出题人点赞

    代码

    #include <bits/stdc++.h>
    #define rin(i,a,b) for(register int i=(a);i<=(b);++i)
    #define irin(i,a,b) for(register int i=(a);i>=(b);--i)
    #define trav(i,a) for(register int i=head[a];i;i=e[i].nxt)
    typedef long long LL;
    using std::cin;
    using std::cout;
    using std::endl;
    
    struct Erkko_istream{
    	inline char getc(){
    		static const int IN_LEN=1<<18|1;
    		static char buf[IN_LEN],*s,*t;
    		return (s==t)&&(t=(s=buf)+fread(buf,1,IN_LEN,stdin)),s==t?-1:*s++;
    	}
    	template<typename _Tp> inline Erkko_istream & operator >> (_Tp &x){
    		static char c11,boo;
    		for(c11=getc(),boo=0;!isdigit(c11);c11=getc()){
    			if(c11==-1) return *this;
    			boo|=c11=='-';
    		}
    		for(x=0;isdigit(c11);c11=getc()) x=x*10+(c11^'0');
    		boo&&(x=-x);
    		return *this;
    	}
    }io;
    
    const int MAXN=100005;
    const LL MOD=998244353;
    int n,m,ecnt,head[MAXN],uu[15],vv[15],cnt;
    int fa[MAXN],dep[MAXN],siz[MAXN],pc[MAXN],_top[MAXN],id[MAXN],tot;
    int arr[45],len,sta[45],top,id2[MAXN],must[45];
    LL f[MAXN][2],k[45][2][2],ans;
    bool vis1[MAXN],vis2[MAXN],ini[MAXN];
    std::vector<int> vec[45];
    struct Edge{
    	int to,nxt;
    }e[(MAXN+10)<<1];
    
    inline void add_edge(int bg,int ed){
    	ecnt++;
    	e[ecnt].to=ed;
    	e[ecnt].nxt=head[bg];
    	head[bg]=ecnt;
    }
    
    inline void add_iedge(int bg,int ed){
    	vec[bg].push_back(ed);
    }
    
    void dfs1(int x,int pre,int depth){
    	fa[x]=pre;
    	dep[x]=depth;
    	siz[x]=1;
    	int maxsiz=-1;
    	trav(i,x){
    		int ver=e[i].to;
    		if(ver==pre) continue;
    		if(siz[ver]){
    			if(x>ver) continue;
    			++cnt;
    			uu[cnt]=x;
    			vv[cnt]=ver;
    			continue;
    		}
    		dfs1(ver,x,depth+1);
    		siz[x]+=siz[ver];
    		if(siz[ver]>maxsiz){
    			maxsiz=siz[ver];
    			pc[x]=ver;
    		}
    	}
    }
    
    void dfs2(int x,int topf){
    	_top[x]=topf;
    	id[x]=++tot;
    	if(!pc[x]) return;
    	dfs2(pc[x],topf);
    	trav(i,x){
    		int ver=e[i].to;
    		if(ver==fa[x]||ver==pc[x]) continue;
    		if(fa[ver]!=x) continue;
    		dfs2(ver,ver);
    	}
    }
    
    inline int lca(int x,int y){
    	while(_top[x]!=_top[y]){
    		if(dep[_top[x]]<dep[_top[y]]) std::swap(x,y);
    		x=fa[_top[x]];
    	}
    	return dep[x]<dep[y]?x:y;
    }
    
    inline bool cmp(int x,int y){
    	return id[x]<id[y];
    }
    
    void build_itree(){
    	rin(i,1,cnt) arr[++len]=uu[i],arr[++len]=vv[i];
    	std::sort(arr+1,arr+len+1,cmp);
    	len=std::unique(arr+1,arr+len+1)-arr-1;
    	int temp=len;
    	rin(i,1,temp-1) arr[++len]=lca(arr[i],arr[i+1]);
    	arr[++len]=1;
    	std::sort(arr+1,arr+len+1,cmp);
    	len=std::unique(arr+1,arr+len+1)-arr-1;
    	rin(i,1,len) id2[arr[i]]=i;
    	top=0;
    	rin(i,1,len){
    		ini[arr[i]]=true;
    		while(top&&id[sta[top]]+siz[sta[top]]-1<id[arr[i]]) add_iedge(id2[sta[top-1]],id2[sta[top]]),top--;
    		sta[++top]=arr[i];
    	}
    	while(top>1) add_iedge(id2[sta[top-1]],id2[sta[top]]),top--;
    }
    
    void dfs3(int x){
    	f[x][0]=f[x][1]=1;
    	trav(i,x){
    		int ver=e[i].to;
    		if(ver==fa[x]) continue;
    		if(fa[ver]!=x) continue;
    		dfs3(ver);
    		f[x][0]=f[x][0]*(f[ver][0]+f[ver][1])%MOD;
    		f[x][1]=f[x][1]*f[ver][0]%MOD;
    	}
    }
    
    void getk(){
    	dfs3(1);
    	add_edge(0,1);
    	irin(i,len,1){
    		int x=arr[i],pre=fa[x],r=1;LL temp[2][2]={1,0,0,0};
    		while((!ini[x]||x==arr[i])&&x){
    			vis1[x]=true;r^=1;
    			if(x!=arr[i]) temp[r][0]=(temp[r^1][0]+temp[r^1][1])%MOD,temp[r][1]=temp[r^1][0];
    			trav(i,x){
    				int ver=e[i].to;
    				if(ver==fa[x]) continue;
    				if(fa[ver]!=x) continue;
    				if(vis1[ver]) continue;
    				temp[r][0]=temp[r][0]*(f[ver][0]+f[ver][1])%MOD;
    				temp[r][1]=temp[r][1]*f[ver][0]%MOD;
    			}
    			pre=fa[pre],x=fa[x];
    		}
    		x=arr[i];
    		k[id2[x]][0][0]=(temp[r][0]+temp[r][1])%MOD;
    		k[id2[x]][1][0]=temp[r][0];
    		pre=fa[x],r=1;
    		memset(temp,0,sizeof temp);
    		temp[0][1]=1;
    		while((!ini[x]||x==arr[i])&&x){
    			vis2[x]=true;r^=1;
    			if(x!=arr[i]) temp[r][0]=(temp[r^1][0]+temp[r^1][1])%MOD,temp[r][1]=temp[r^1][0];
    			trav(i,x){
    				int ver=e[i].to;
    				if(ver==fa[x]) continue;
    				if(fa[ver]!=x) continue;
    				if(vis2[ver]) continue;
    				temp[r][0]=temp[r][0]*(f[ver][0]+f[ver][1])%MOD;
    				temp[r][1]=temp[r][1]*f[ver][0]%MOD;
    			}
    			pre=fa[pre],x=fa[x];
    		}
    		x=arr[i];
    		k[id2[x]][0][1]=(temp[r][0]+temp[r][1])%MOD;
    		k[id2[x]][1][1]=temp[r][0];
    	}
    }
    
    void dfs4(int x){
    	f[x][0]=f[x][1]=1;
    	rin(i,0,(int)vec[x].size()-1){
    		int ver=vec[x][i];
    		dfs4(ver);
    		f[x][0]=f[x][0]*((f[ver][0]*k[ver][0][0]+f[ver][1]*k[ver][0][1])%MOD)%MOD;
    		f[x][1]=f[x][1]*((f[ver][0]*k[ver][1][0]+f[ver][1]*k[ver][1][1])%MOD)%MOD;
    	}
    	if(must[x]==0) f[x][1]=0;
    	else if(must[x]==1) f[x][0]=0;
    }
    
    void solve(){
    	rin(i,0,(1<<cnt)-1){
    		memset(must,-1,sizeof must);
    		bool flag=false;
    		rin(j,1,cnt){
    			int temp=((i>>(j-1))&1);
    			if(must[id2[uu[j]]]>-1&&must[id2[uu[j]]]!=temp){
    				flag=true;
    				break;
    			}
    			must[id2[uu[j]]]=temp;
    			if(must[id2[uu[j]]]==1){
    				if(must[id2[vv[j]]]==1){
    					flag=true;
    					break;
    				}
    				must[id2[vv[j]]]=0;
    			}
    		}
    		if(flag) continue;
    		dfs4(1);
    		ans=(ans+f[1][0]*k[1][0][0]+f[1][1]*k[1][0][1])%MOD;
    	}
    }
    
    int main(){
    #ifndef ONLINE_JUDGE
    	freopen("duliu.in","r",stdin);
    	freopen("duliu.out","w",stdout);
    #endif
    	io>>n>>m;
    	rin(i,1,m){
    		int u,v;
    		io>>u>>v;
    		add_edge(u,v);
    		add_edge(v,u);
    	}
    	dfs1(1,0,1);
    	dfs2(1,1);
    	build_itree();
    	getk();
    	solve();
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    [CF966E]May Holidays

    分析

    虚树和根号重构!

    原题可以转化为:

    1. 将结点(x)到根的所有结点权值(+1/-1),并给结点(x)打上/去掉标记。

    2. 查询有多少个结点的权值为负且没有打标记。

    考虑根号重构,每根号个操作重构虚树。虚树边,也就是原树上和这根号个修改无关的链可以一起处理。对于一条无关链上的所有结点的权值,直接排序后unique(),记一下每种权值的个数,维护一个指针指向第一个权值(geq 0)的位置,通过类似于打lazy标记的方式进行修改。修改的同时维护(ans)就好了。

    也是超多细节,实现起来略微丧心病狂。

    代码

    #include <bits/stdc++.h>
    #define rin(i,a,b) for(register int i=(a);i<=(b);++i)
    #define irin(i,a,b) for(register int i=(a);i>=(b);--i)
    #define trav(i,a) for(register int i=head[a];i;i=e[i].nxt)
    typedef long long LL;
    using std::cin;
    using std::cout;
    using std::endl;
    
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    }
    
    const int MAXN=100005;
    int n,m,ecnt,head[MAXN];
    int fa[MAXN],dep[MAXN],siz[MAXN],pc[MAXN],_top[MAXN],id[MAXN],tot;
    int t[MAXN],opt[505],cnt,arr[1005],len,sta[1005],top;
    int ifa[MAXN],ptr[MAXN],tag[MAXN],ans;
    bool ini[MAXN],onv[MAXN];
    std::vector<int> buc[MAXN],num[MAXN];
    struct Edge{
    	int to,nxt;
    }e[MAXN];
    
    inline void add_edge(int bg,int ed){
    	ecnt++;
    	e[ecnt].to=ed;
    	e[ecnt].nxt=head[bg];
    	head[bg]=ecnt;
    }
    
    inline void add_iedge(int bg,int ed){
    	ifa[ed]=bg;
    }
    
    void dfs1(int x,int pre,int depth){
    	fa[x]=pre;
    	dep[x]=depth;
    	siz[x]=1;
    	int maxsiz=-1;
    	trav(i,x){
    		int ver=e[i].to;
    		dfs1(ver,x,depth+1);
    		siz[x]+=siz[ver];
    		if(siz[ver]>maxsiz){
    			maxsiz=ver;
    			pc[x]=ver;
    		}
    	}
    }
    
    void dfs2(int x,int topf){
    	_top[x]=topf;
    	id[x]=++tot;
    	if(!pc[x]) return;
    	dfs2(pc[x],topf);
    	trav(i,x){
    		int ver=e[i].to;
    		if(ver==pc[x]) continue;
    		dfs2(ver,ver);
    	}
    }
    
    inline int lca(int x,int y){
    	while(_top[x]!=_top[y]){
    		if(dep[_top[x]]<dep[_top[y]]) std::swap(x,y);
    		x=fa[_top[x]];
    	}
    	return dep[x]<dep[y]?x:y;
    }
    
    inline bool cmp(int x,int y){
    	return id[x]<id[y];
    }
    
    void build_itree(){
    	rin(i,1,cnt) arr[i]=std::abs(opt[i]);
    	len=cnt;
    	std::sort(arr+1,arr+len+1,cmp);
    	len=std::unique(arr+1,arr+len+1)-arr-1;
    	int temp=len;
    	rin(i,1,temp-1) arr[++len]=lca(arr[i],arr[i+1]);
    	arr[++len]=1;
    	std::sort(arr+1,arr+len+1,cmp);
    	len=std::unique(arr+1,arr+len+1)-arr-1;
    	top=0;
    	rin(i,1,len){
    		ini[arr[i]]=true;
    		while(top&&id[sta[top]]+siz[sta[top]]-1<id[arr[i]]) add_iedge(sta[top-1],sta[top]),top--;
    		sta[++top]=arr[i];
    	}
    	while(top>1) add_iedge(sta[top-1],sta[top]),top--;
    }
    
    void solve(){
    	rin(i,1,len){
    		int x=arr[i],cur=x;
    		while(x&&(!ini[x]||x==cur)){
    			if(x!=cur&&!onv[x]) buc[cur].push_back(t[x]);
    			x=fa[x];
    		}
    		sort(buc[cur].begin(),buc[cur].end());
    		rin(j,0,(int)buc[cur].size()-1){
    			if(j==0||buc[cur][j]!=buc[cur][j-1]){
    				num[cur].push_back(0),buc[cur][num[cur].size()-1]=buc[cur][j];
    				if(buc[cur][j]<0) ptr[cur]++;
    			}
    			++num[cur][num[cur].size()-1];
    		}
    		while(buc[cur].size()>num[cur].size()) buc[cur].pop_back();
    	}
    	rin(i,1,cnt){
    		int x=opt[i],cur=abs(x);
    		if(x>0){
    			onv[x]=true;
    			if(t[x]<0) ans--;
    			while(x){
    				t[x]--;tag[x]--;
    				if(ptr[x]!=buc[x].size()&&buc[x][ptr[x]]+tag[x]<0) ans+=num[x][ptr[x]],ptr[x]++;
    				if(t[x]==-1&&!onv[x]) ans++;
    				x=ifa[x];
    			}
    		}
    		else{
    			onv[x=-x]=false;
    			if(t[x]+1<0) ans++;
    			while(x){
    				t[x]++;tag[x]++;
    				if(ptr[x]&&buc[x][ptr[x]-1]+tag[x]>=0) --ptr[x],ans-=num[x][ptr[x]];
    				if(t[x]==0&&!onv[x]&&x!=cur) ans--;
    				x=ifa[x];
    			}
    		}
    		printf("%d ",ans);
    	}
    	rin(i,1,len){
    		int x=arr[i],cur=x;
    		while(x&&(!ini[x]||x==cur)){
    			if(x!=cur) t[x]+=tag[cur];
    			x=fa[x];
    		}
    	}
    	rin(i,1,len){
    		int x=arr[i];
    		buc[x].clear(),num[x].clear(),ifa[x]=ptr[x]=tag[x]=0,ini[x]=false;
    	}
    	len=0;
    }
    
    int main(){
    	n=read(),m=read();
    	rin(i,2,n) add_edge(read(),i);
    	rin(i,1,n) t[i]=read();
    	dfs1(1,0,1);
    	dfs2(1,1);
    	int lim=sqrt(m)+0.5;
    	rin(i,1,m){
    		opt[++cnt]=read();
    		if(cnt==lim){
    			build_itree();
    			solve();
    			cnt=0;
    		}
    	}
    	if(cnt){
    		build_itree();
    		solve();
    	}
    	printf("
    ");
    	return 0;
    }
    

    [CF1073G]Surprise me!

    暂时不想写了ToT。

  • 相关阅读:
    java基础 -- 经典排序
    java 基础 ----- Arrays 工具类
    java基础 ---- 二维数组
    java基础 ---- 一维数组
    java基础 ---- 练习for循环
    AI 偏微分方程
    AI 反向传播神经网络
    AI 判别式模型和生成式模型
    AI 隐含狄利克雷分布
    AI 朴素贝叶斯分类
  • 原文地址:https://www.cnblogs.com/ErkkiErkko/p/10284733.html
Copyright © 2011-2022 走看看