zoukankan      html  css  js  c++  java
  • Codechef EDGEST 树套树 树状数组 线段树 LCA 卡常

    原文链接http://www.cnblogs.com/zhouzhendong/p/9016579.html

    题目传送门 - Codechef EDGEST

    题意

      给定相同点集上的两棵生成树$T_1$和$T_2$,节点编号为$1$∼$N$。对于$T_1$中的每条边$e_1$,你需要求在$T_2$中有多少条边$e_2$满足:

      • $T_1 − e_1 + e_2$(从$T_1$中删去$e_1$再加上$e_2$构成的图)是一棵生成树;

      • $T_2 − e_2 + e_1$ 也是一棵生成树。

      $1 ≤ T ≤ 10 , 2 ≤ N ≤ 2 imes 10^5 , ∑N ≤ 2 imes  10^5$

    题解

      首先,给这两棵无根树定根为$1$。

      给$T_1$进行$dfs$,设“时间”表示当前已经访问的节点数,记节点$i$第一次访问的时间为$in_i$(即其$dfs$序)(要计入节点$i$),退出节点$i$的时间为$out_i$。

      考虑删除$T_1$的一条边$y_1 ightarrow x_1$,其中$y_1$为$x_1$的父亲。

      (1)则需要添加的边必定是$x_2$这棵子树中的节点对这棵子树外的节点的连边。即$forall ein T_2 , e=(x_2,y_2)$且($(in_{x_2}in [in_{x_1},out_{x_1}]且in_{y_2}in [1,in_{x_1})cup(out_{x_1},n])$或$(in_{y_2}in [in_{x_1},out_{x_1}]且in_{x_2}in [1,in_{x_1})cup(out_{x_1},n])$)。

      考虑在$T_2$中,连接了$x_2$和$y_2$,则要删除的必定是$x_1$到$y_1$路径上的边。

      其中,$x_1$到$y_1$的路径可以拆分成$x_1$到$T_2$根路径、$y_1$到根路径,以及两条$LCA_{T_2}(x_1,y_1)$到根的负路径。

      在$T_1$中,我们要计算$x_1$这棵子树中的节点对这棵子树外的节点的连边,并且这个连边在$T_2$中存在。

      于是梳理完上面的这些东西之后,我们可以开始说做法了。

      我们用树套树维护(1)要弄的东西。

      首先预处理在$T_1$中的每一对$(x_1,y_1)$需要查询的询问区间(即(1)中所指)、询问($T_2$中)哪一个节点$(x_1,y_1和LCA_{T_2}(x_1,y_1)$到根路径的贡献,把这些询问分别放到对应节点所准备的$vector$里面。

      然后对$T2$进行$dfs$,对于$T_2$的每一个节点,每一个询问分别用当前树套树状态来查询,并在$dfs$的过程中修改树套树。

      时间复杂度$O(nlog^2 n)$。但是常数较大,要$TLE$。

      于是我们把树套树的第一维写成树状数组,再卡一波常数就可以通过了。

      代码长到吐。

    代码

    #include <bits/stdc++.h>
    #pragma GCC optimize ("Ofast")
    #define time __time
    using namespace std;
    const int N=200005;
    int lowbit(int x){
    	return x&-x;
    }
    __inline char gc() {
        static char buf[300000], *p1 = buf, *p2 = buf;
        return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 300000, stdin), p1 == p2) ? EOF : *p1++;
    }
    //#define gc getchar
    __inline int read(){
    	int x=0;
    	char ch=gc();
    	while (!isdigit(ch))
    		ch=gc();
    	while (isdigit(ch))
    		x=(x<<1)+(x<<3)+ch-48,ch=gc();
    	return x;
    }
    int T,n,root[N],fa[N],in[N],out[N],time,ans[3][N];
    int anst[N][20],depth[N];
    struct pr{
    	int first,second;
    	pr(){}
    	pr(int a,int b){
    		first=a,second=b;
    	}
    };
    vector <pr> q[N];
    struct Gragh{
    	int cnt,x[N*2],y[N*2],nxt[N*2],fst[N];
    	void clear(){
    		cnt=0;
    		memset(fst,0,sizeof fst);
    	}
    	void add(int a,int b){
    		y[++cnt]=b,x[cnt]=a,nxt[cnt]=fst[a],fst[a]=cnt;
    	}
    }a,b;
    struct Node{
    	int v,lc,rc;
    }t[N*800];
    int STsize;
    __inline void update(int &rt,int L,int R,int x,int d){
    	if (!rt){
    		rt=++STsize;
    		t[rt].v=t[rt].lc=t[rt].rc=0;
    	}
    	t[rt].v+=d;
    	if (L==R)
    		return;
    	int mid=(L+R)>>1;
    	if (x<=mid)
    		update(t[rt].lc,L,mid,x,d);
    	else
    		update(t[rt].rc,mid+1,R,x,d);
    }
    __inline int query(int &rt,int L,int R,int xL,int xR){
    	if (!rt||L>xR||R<xL)
    		return 0;
    	if (xL<=L&&R<=xR)
    		return t[rt].v;
    	int mid=(L+R)>>1;
    	if (xR<=mid)
    		return query(t[rt].lc,L,mid,xL,xR);
    	if (xL>mid)
    		return query(t[rt].rc,mid+1,R,xL,xR);
    	return query(t[rt].lc,L,mid,xL,mid)+query(t[rt].rc,mid+1,R,mid+1,xR);
    }
    __inline void update(int rt,int L,int R,int x,int y,int d){
    	for (int i=x;i<=n;i+=i&-i)
    		update(root[i],1,n,y,d);
    }
    __inline int query(int rt,int L,int R,int xL,int xR,int yL,int yR){
    	int ans=0;
    	for (int i=xR;i;i-=i&-i)
    		ans+=query(root[i],1,n,yL,yR);
    	for (int i=xL-1;i;i-=i&-i)
    		ans-=query(root[i],1,n,yL,yR);
    	return ans;
    }
    __inline void change(int x,int y,int d){
    	x=in[x],y=in[y];
    	if (x>y)
    		swap(x,y);
    	update(1,1,n,x,y,d);
    }
    __inline int ask(int x,int y){
    	int ans=0;
    	if (1<=x-1)
    		ans+=query(1,1,n,1,x-1,x,y);
    	if (y+1<=n)
    		ans+=query(1,1,n,x,y,y+1,n);
    	return ans;
    }
    __inline void LCA_prepare(int x,int pre){
    	depth[x]=depth[pre]+1;
    	anst[x][0]=pre;
    	for (int i=1;i<20;i++)
    		anst[x][i]=anst[anst[x][i-1]][i-1];
    	for (int i=b.fst[x];i;i=b.nxt[i])
    		if (b.y[i]!=pre)
    			LCA_prepare(b.y[i],x);
    }
    __inline int LCA(int x,int y){
    	if (depth[x]<depth[y])
    		swap(x,y);
    	for (int i=19;i>=0;i--)
    		if (depth[anst[x][i]]>=depth[y])
    			x=anst[x][i];
    	if (x==y)
    		return x;
    	for (int i=19;i>=0;i--)
    		if (anst[x][i]!=anst[y][i])
    			x=anst[x][i],y=anst[y][i];
    	return anst[x][0];
    }
    __inline void dfsa(int x,int pre){
    	fa[x]=pre;
    	in[x]=++time;
    	if (pre){
    		q[x].push_back(pr(x,0));
    		q[pre].push_back(pr(x,1));
    		q[LCA(x,pre)].push_back(pr(x,2));
    	}
    	for (int i=a.fst[x];i;i=a.nxt[i])
    		if (a.y[i]!=pre)
    			dfsa(a.y[i],x);
    	out[x]=time;
    }
    __inline void dfsb(int x,int pre){
    	if (pre)
    		change(x,pre,1);
    /*	for (int i=0;i<q[x].size();i++){
    		int y=q[x][i].first,t=q[x][i].second;
    		ans[t][y]=ask(in[y],out[y]);
    	}*/
    	while (!q[x].empty()){
    		int y=q[x].back().first,t=q[x].back().second;
    		ans[t][y]=ask(in[y],out[y]);
    		q[x].pop_back();
    	}
    	for (int i=b.fst[x];i;i=b.nxt[i])
    		if (b.y[i]!=pre)
    			dfsb(b.y[i],x);
    	if (pre)
    		change(x,pre,-1);
    }
    int main(){
    	T=read();
    	while (T--){
    		n=read();
    		STsize=0;
    		memset(root,0,sizeof root);
    		a.clear(),b.clear();
    		for (int i=1,x,y;i<n;i++){
    			x=read(),y=read();
    			a.add(x,y),a.add(y,x);
    		}
    		for (int i=1,x,y;i<n;i++){
    			x=read(),y=read();
    			b.add(x,y),b.add(y,x);
    		}
    		for (int i=0;i<=n;i++)
    			for (int j=0;j<20;j++)
    				anst[i][j]=0;
    		LCA_prepare(1,0);
    		for (int i=1;i<=n;i++)
    			q[i].clear();
    		time=0;
    		dfsa(1,0);
    		dfsb(1,0);
    		for (int i=1;i<=(n-1)*2;i+=2){
    			int x=a.x[i],y=a.y[i];
    			if (fa[y]==x)
    				swap(x,y);
    			printf("%d ",ans[0][x]+ans[1][x]-2*ans[2][x]);
    		}
    		puts("");
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    动态规划 01背包问题
    日常水题 蓝桥杯基础练习VIP-字符串对比
    本博客导航
    2019 ICPC 南昌 (C E G L)
    [模板]线段树
    [模板]手写双端队列(或普通队列)
    2019 ICPC Asia Yinchuan Regional (G, H)
    与超级源点与超级汇点相关的两题POJ 1062, HDU 4725
    [模板]链式向前星
    [总结]关于反向建图
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/CC-EDGEST.html
Copyright © 2011-2022 走看看