【双树问题-树剖+线段树合并】CC_EDGEST Edges in Spanning Trees

【题目】
Codechef
给定相同点集(大小为 n n n)上两棵生成树 T 1 , T 2 T_1,T_2 T1,T2。对于 T 1 T_1 T1中每条边,求 T 2 T_2 T2中有多少条边满足:

  • T 1 − e 1 + e 2 T_1-e_1+e_2 T1e1+e2(从 T 1 T_1 T1中删去 e 1 e_1 e1再加上 e 2 e_2 e2)是一棵生成树
  • T 2 − e 2 + e 1 T_2-e_2+e_1 T2e2+e1是一棵生成树

n ≤ 2 × 1 0 5 n\leq 2\times 10^5 n2×105

【解题思路】
以下每个点维护父边信息。

若两条边可以互相替代,则满足 e 1 e_1 e1 T 1 T_1 T1 e 2 e_2 e2两端点的路径上, e 2 e_2 e2同理。

我们不妨只考虑第一个限制,那么这个问题就是一个树链 + 1 +1 +1的问题,怎么做都可以,这里可以使用树上差分,即在路径端点处打上 + 1 +1 +1,在 l c a lca lca处打上 − 2 -2 2

现在加入第二个限制,实际上就是要在第一个限制的基础上求出有多少条边满足第二个限制,即 e 1 e_1 e1 T 2 T_2 T2上的路径中有多少条边在差分中计算到了。实际上我们在差分的时候,每遇到一个 + + +标记,我们可以在其 T 2 T_2 T2的对应边上 + 1 +1 +1,每遇到一个 − - 标记,那么就在对应边上 − 1 -1 1。这样做的话, e 1 e_1 e1在上的 T 2 T_2 T2路径所贡献到的边,实际上就是对应路径的权值。

总的来说,我们要做的,就是对 T 1 T_1 T1进行 dfs \text{dfs} dfs和打标记,对 T 2 T_2 T2进行单点修改,路径查询。而在 dfs \text{dfs} dfs过程中, T 2 T_2 T2的线段树我们要进行线段树合并,因为每个节点得到的树剖线段树是独立的。

路径查询写一个树链剖分就可以了,复杂度 O ( n log ⁡ 2 n ) O(n\log ^2 n) O(nlog2n)
就是要写一会。

【参考代码】

#include<bits/stdc++.h>
#define pb push_back
using namespace std;

const int N=2e5+10,M=N*60;

namespace IO
{
	int read()
	{
		int ret=0;char c=getchar();
		while(!isdigit(c)) c=getchar();
		while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
		return ret;
	}
	void write(int x){if(x>9)write(x/10);putchar(x%10^48);}
	void writesp(int x){write(x);putchar(' ');}
}
using namespace IO;

namespace Segment
{
	int rt[N];
	struct tr
	{
		int sz,sum[M],ls[M],rs[M];
		void update(int &x,int l,int r,int p,int v)
		{
			if(!x) x=++sz;sum[x]+=v;
			if(l==r) return;
			int mid=(l+r)>>1;
			if(p<=mid) update(ls[x],l,mid,p,v);
			else update(rs[x],mid+1,r,p,v);
		}
		int query(int x,int l,int r,int L,int R)
		{
			//printf("Q:%d %d %d %d %d\n",x,l,r,L,R);
			if(L>R || !x) return 0;
			if(L<=l && r<=R) return sum[x];
			int mid=(l+r)>>1,res=0;
			if(L<=mid) res+=query(ls[x],l,mid,L,R);
			if(R>mid) res+=query(rs[x],mid+1,r,L,R);
			return res;
		}
		int merge(int x,int y,int l,int r)
		{
			if(!x || !y) return x+y;
			int mid=(l+r)>>1,z=++sz;
			sum[z]=sum[x]+sum[y];
			if(l^r)	ls[z]=merge(ls[x],ls[y],l,mid),rs[z]=merge(rs[x],rs[y],mid+1,r);
			return z;
		}
		void clear()
		{
			for(int i=0;i<=sz;++i) sum[i]=ls[i]=rs[i]=0;
			sz=0;
		}
		void print(int x,int l,int r)
		{
			printf("%d %d %d %d\n",x,l,r,sum[x]);
			if(l==r) return;
			int mid=(l+r)>>1;
			print(ls[x],l,mid);print(rs[x],mid+1,r);
		}
	}tr;
}
using namespace Segment;

namespace Tree
{
	struct Tway{int v,nex,id;};
	struct Tree
	{
		int tot,ind;
		int head[N],top[N],son[N],fa[N],siz[N],pos[N],dep[N];
		Tway e[N<<1];
		void add(int u,int v,int id)
		{
			e[++tot]=(Tway){v,head[u],id};head[u]=tot;
			e[++tot]=(Tway){u,head[v],id};head[v]=tot;
		}
		void dfs1(int x)
		{
			siz[x]=1;
			for(int i=head[x];i;i=e[i].nex)
			{
				int v=e[i].v;
				if(v==fa[x]) continue;
				fa[v]=x;dep[v]=dep[x]+1;dfs1(v);siz[x]+=siz[v];
				if(siz[v]>siz[son[x]]) son[x]=v;
			}
		}
		void dfs2(int x,int tp)
		{
			top[x]=tp;pos[x]=++ind;
			if(son[x]) dfs2(son[x],tp);
			for(int i=head[x];i;i=e[i].nex)
			{
				int v=e[i].v;
				if(v==fa[x] || v==son[x]) continue;
				dfs2(v,v);
			}
		}
		void build(){dfs1(1);dfs2(1,1);}
		int lca(int x,int y)
		{
			while(top[x]^top[y])
			{
				if(dep[top[x]]>=dep[top[y]])x=fa[top[x]];
				else y=fa[top[y]];
			}
			return dep[x]<dep[y]?x:y;
		}
		void clear()
		{
			for(int i=0;i<=ind;++i) head[i]=top[i]=dep[i]=fa[i]=siz[i]=son[i]=pos[i]=0;
			tot=ind=0;
		}
	}T1,T2;
}
using namespace Tree;

namespace DreamLolita
{
	int n,ans[N],fr[N];
	vector<int>tag[N];
	int querychain(int root,int x,int y)
	{
		//printf("query:%d %d\n",x,y);
		int res=0;
		while(T2.top[x]^T2.top[y])
		{
			if(T2.dep[T2.top[x]]<T2.dep[T2.top[y]]) swap(x,y);//should jump x
			res+=tr.query(root,1,n,T2.pos[T2.top[x]],T2.pos[x]);x=T2.fa[T2.top[x]];
		}
		if(T2.dep[x]<T2.dep[y]) swap(x,y);
		//printf("%d %d %d\n",root,T2.pos[y]+1,T2.pos[x]);
		res+=tr.query(root,1,n,T2.pos[y]+1,T2.pos[x]);//-1 because no lca
		return res;
	}
	void dfstag(int x)//put tag on T1,so dfs T2
	{
		for(int i=T2.head[x];i;i=T2.e[i].nex)
		{
			int v=T2.e[i].v,d=T2.e[i].id;
			if(v==T2.fa[x]) continue;
			tag[v].pb(d);tag[x].pb(d);tag[T1.lca(x,v)].pb(-d);
			fr[d]=v;dfstag(v);//point v maintain edge d on T2
		}
	}
	void dfs(int x,int d)//calc ans,so dfs T1,and add  on T2,query on T2,use Heavy_Light cut
	{
		for(int i=T1.head[x];i;i=T1.e[i].nex)//first dfs then calc
		{
			int v=T1.e[i].v;
			if(v==T1.fa[x]) continue; 
			dfs(v,T1.e[i].id);rt[x]=tr.merge(rt[x],rt[v],1,n);
		}
		if(x==1) return;
		for(auto i:tag[x])//push tag,+1 or -2
		{
			if(i>0) tr.update(rt[x],1,n,T2.pos[fr[i]],1);
			else tr.update(rt[x],1,n,T2.pos[fr[-i]],-2);
		}
		//printf("now:%d\n",x);tr.print(rt[x],1,n);puts("");
		ans[d]=querychain(rt[x],x,T1.fa[x]);
	}
	void clear()
	{
		T1.clear();T2.clear();tr.clear();
		for(int i=0;i<=n;++i) tag[i].clear(),fr[i]=0,rt[i]=0;
	}
	void solution()
	{
		n=read();
		for(int i=1;i<n;++i) T1.add(read(),read(),i);
		for(int i=1;i<n;++i) T2.add(read(),read(),i);
		T1.build();T2.build();dfstag(1);dfs(1,0);
		for(int i=1;i<n;++i) writesp(ans[i]); puts("");
		clear();
	}
}


int main()
{
#ifndef ONLINE_JUDGE
	freopen("CC_EDGEST.in","r",stdin);
	freopen("CC_EDGEST.out","w",stdout);
#endif
	int T=read();
	while(T--) DreamLolita::solution();
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值