1 条题解

  • 0
    @ 2022-8-26 10:05:59
    #include <cstring>
    #include <cstdio>
    const int MX=100005;
    int n,ind,fir[MX],deep[MX],idx[MX],topf[MX],son[MX],sz[MX],fa[MX];
    struct Edge{int to,nxt;}e[MX<<1];
    struct Node{int lc,rc,cnt;void cln(){lc=rc=cnt=0;}};
    //Node结构体中:lc左颜色,rc右颜色,cnt个数 
    struct Segtree{
    	int lazy[MX<<2];Node tree[MX<<2];
    	void cln(){
    		//线段树内清空 
    	    memset(lazy,0,sizeof lazy);
    		for(int i=0,h=MX<<2;i<h;i++)tree[i].cln();	
    	}
    	void lazy_down(int k,int l,int r){
    		//懒标记下传 
    		if(!lazy[k])return;
    		int x=lazy[k],mid=(l+r)>>1;lazy[k]=0;
    		lazy[k<<1]=lazy[k<<1|1]=x;
    		tree[k<<1]=(Node){x,x,mid-l};
    		//为什么是mid-l?因为一共有mid-l+1个点都被染成颜色x,所以考虑间隔有mid-l对 
    		tree[k<<1|1]=(Node){x,x,r-mid-1};
    		//为什么是r-mid-1?理由同上啦 
    	}
    	void update(int ul,int ur,int nl,int nr,int pos,int num){
    		if(ul<=nl&&nr<=ur){
    			tree[pos]=(Node){num,num,nr-nl};
    			//为什么是nr-nl?理由在lazy_down函数里啦 
    			lazy[pos]=num;return;
    		}
    		lazy_down(pos,nl,nr);int mid=(nl+nr)>>1;
    		if(ul<=mid)update(ul,ur,nl,mid,pos<<1,num);
    		if(mid<ur)update(ul,ur,mid+1,nr,pos<<1|1,num);
    		Node ls=tree[pos<<1],rs=tree[pos<<1|1];
    		tree[pos]=(Node){
    			ls.lc,rs.rc,ls.cnt+rs.cnt+(ls.rc==rs.lc)
    		};
    		//关键↑区间合并,应该比较好懂,关键在于左儿子的右颜色
    		//和右儿子的左颜色相同时,需要将计数器加一 
    	}
    	Node query(int al,int ar,int nl,int nr,int pos){
    		if(al<=nl&&nr<=ar)return tree[pos];
    		lazy_down(pos,nl,nr);int cnt=0,mid=(nl+nr)>>1;Node w1,w2;
    		//cnt负责控制查询情况种类,见下↓ 
    		if(al<=mid){cnt++;w1=query(al,ar,nl,mid,pos<<1);}
    		if(mid<ar){cnt+=2;w2=query(al,ar,mid+1,nr,pos<<1|1);}
    		if(cnt==1)return w1;if(cnt==2)return w2;
    		return (Node){
    			w1.lc,w2.rc,w1.cnt+w2.cnt+(w1.rc==w2.lc)
    		};
    		//同样是关键的合并,同update函数操作 
    	}
    }sgtree;
    void add(int a,int b,int pos){
    	e[pos]=(Edge){b,fir[a]};fir[a]=pos;
    }
    //建边 
    void swp(int& a,int& b){int t=a;a=b;b=t;}
    //手写交换swap函数 
    void all_cln(){
    	//这个函数是掌管清空的,多组数据一定要小心!
    	//建议:能清的都清了,免得出现什么奇怪的错误 
    	ind=0;sgtree.cln();
    	memset(sz,0,sizeof sz);
    	memset(fa,0,sizeof fa);
    	memset(idx,0,sizeof idx);
    	memset(fir,0,sizeof fir);
    	memset(son,0,sizeof son);
    	memset(topf,0,sizeof topf);
    	memset(deep,0,sizeof deep);
    }
    void dfs1(int x,int f,int d){
    	deep[x]=d;fa[x]=f;sz[x]=1;
    	for(int i=fir[x];i;i=e[i].nxt){
    		int h=e[i].to;
    		if(h!=f){
    			dfs1(h,x,d+1);sz[x]+=sz[h];
    			if(sz[h]>sz[son[x]])son[x]=h;
    		}
    	}
    }
    //树剖常规第一遍dfs 
    void dfs2(int x,int tp){
    	if(!x)return;topf[x]=tp;
    	idx[x]=++ind;dfs2(son[x],tp);
    	for(int i=fir[x];i;i=e[i].nxt){
    		int h=e[i].to;if(h!=son[x]&&h!=fa[x])dfs2(h,h);
    	}
    }
    //树剖常规第二遍dfs 
    void range_update(int x,int y,int num){
    	while(topf[x]!=topf[y]){
    		int tx=topf[x],ty=topf[y];
    		if(deep[tx]<deep[ty]){swp(x,y);swp(tx,ty);}
    		sgtree.update(idx[tx],idx[x],1,n,1,num);
    		x=fa[tx];
    	}
    	if(deep[x]<deep[y])swp(x,y);
    	sgtree.update(idx[y],idx[x],1,n,1,num);
    }
    //树剖区间修改 
    int range_query(int x,int y){
    	//树剖的区间查询,可以说是整个程序的大核心,一定要深刻理解
    	//建议画图辅助思考,会有一定难度 
    	bool flg=0;//flg表示当前答案应归到哪边,0为ans1,1为ans2 
    	Node h,ans1=(Node){0,0,0},ans2=(Node){0,0,0};
    	while(topf[x]!=topf[y]){
    		int tx=topf[x],ty=topf[y];
    		if(deep[tx]<deep[ty]){flg=!flg;swp(tx,ty);swp(x,y);}
    		//记得同时取反flg 
    		h=sgtree.query(idx[tx],idx[x],1,n,1);
    		//以下是最核心部分↓
    		//请大家一定注意:每次的查询h,一定是左端点在深度小的地方,
    		//右端点在深度大的地方,所以千万不能把左右端点合并错 
    		if(flg)
    		    ans2=(Node){
    				h.lc,ans2.rc,ans2.cnt+h.cnt+(ans2.lc==h.rc)
    			};
    		//ans2情况 
    		else
    			ans1=(Node){
    				ans1.lc,h.lc,ans1.cnt+h.cnt+(ans1.rc==h.rc)
    			};
    		//ans1情况 
    		x=fa[tx];
    	}
    	if(deep[x]<deep[y]){swp(x,y);flg=!flg;}
    	h=sgtree.query(idx[y],idx[x],1,n,1);
    	if(flg)
    	    ans2=(Node){
    			h.lc,ans2.rc,ans2.cnt+h.cnt+(ans2.lc==h.rc)
    		};
    	else
    		ans1=(Node){
    			ans1.lc,h.lc,ans1.cnt+h.cnt+(ans1.rc==h.rc)
    		};
    	//末处理 
    	return ans1.cnt+ans2.cnt+(ans1.rc==ans2.lc);
    }
    int main(){
    	//freopen("edge.in","r",stdin);
        //freopen("edge.out","w",stdout);
    	int data;scanf("%d",&data);
    	for(int i=1;i<=data;i++){
    		all_cln();//别忘了清空!! 
    		int m;scanf("%d%d",&n,&m);
    		for(int j=1;j<n;j++){
    			int a,b;scanf("%d%d",&a,&b);
    			add(a,b,j);add(b,a,j+n-1);
    		}
    		dfs1(1,0,1);dfs2(1,1);
    		for(int j=1;j<=n;j++)sgtree.update(idx[j],idx[j],1,n,1,-idx[j]);
    		//上面这句也很重要,先把每个点起始赋一个互不相同的值,
    		//随便你赋啥 
    		for(int j=1;j<=m;j++){
    		    int opt,a,b;
    			scanf("%d%d%d",&opt,&a,&b);
    			if(opt&1)range_update(a,b,j);
    			//以询问编号j作为“独一无二的颜色” 
    			else printf("%d\n",range_query(a,b));
    		}
    	}
    	//fclose(stdin);
        //fclose(stdout);
    	return 0;
    }
    
    • 1

    信息

    ID
    132
    时间
    1000ms
    内存
    1024MiB
    难度
    8
    标签
    递交数
    70
    已通过
    10
    上传者