其他分享
首页 > 其他分享> > P5327 [ZJOI2019]语言

P5327 [ZJOI2019]语言

作者:互联网

 

 P5327 [ZJOI2019]语言

题目描述

详见:P5327 [ZJOI2019]语言

简要题意:给定一棵树和一些链,问树上处于同一条链的不同点对数。

Solution

对于每一个点i,考虑以它为端点的可行路径有哪些。

我们可以发现,i可以到达的节点会组成一个斯坦纳树,这棵斯坦纳树包含了i即经过i链。

我们进一步可知,这棵斯坦纳树就是以i和经过i的所有链的端点u,v为关键点的最小斯坦纳树。

所以我们考虑对于每一个点,维护经过它的链组成的斯坦纳树是什么,顺便维护答案(斯坦纳树的边数)。

 

事实上,斯坦纳树的边数就是其中所有点的深度-dfs序相邻的点的LCA的深度,如下:

设斯坦纳树的点集为V,记i的dfs序为  dfn[i],深度为  dep[i]  。

若满足  \forall_{i<j}\;\;dfn[V[i]]<dfn[V[j]]  ,即dfs序按升序排序,则

ans_u=\sum_{i}{dep[V[i]]}-\sum_{i}{dep[lca(V[i],V[i+1])]}-dep[lca(V[k],V[1])]

Ans=\sum ans_i

 

可以用线段树合并(启发式合并也可)支持上述信息的维护。

线段树的下标为  dfn,并记录  [l,r]  中dfn最大的点ladfn 最小的点fi,以及 ans  。

合并时   ans_{l,r}=ans_{l,mid}+ans_{mid+1,r}-dep[lca(la_{l,mid},fi_{mid+1,r})]

统计答案时  Ans+=ans_{l,r}-dep[lca(fi_{l,r},la_{l,r})] 

 

因此,对于每一条路径  (u,v)  ,我们可以用树上差分把它拆成  (1,u),(1,v),(1,lca),(1,fa[lca])  这四条代价分别为+1,+1,-1,-1的链,分别放入权值线段树,并不断向上合并到根,沿路统计答案即可。

st表实现LCA,时间复杂度  O(nlgn)

如果写倍增LCA的会多一个log,卡卡常应该也能过。

 

PS:这道题写了1h,调了5h,原因是线段树动态开点要开log倍的内存。但我一直以为是dfs爆栈了(因为今早有一题先例),由于要用到dfs序,所以甚至在try coding 手工栈,However,WA声依旧,改得代码奇丑无比,自闭了一个下午,最后柳暗花明又一村,有一种想右转直行的冲动(五楼机房右边是窗)。

Code

最后贴一个丑陋的代码(早把罪恶的手工栈删了)。

#include<bits/stdc++.h>
#define RG register
typedef long long ll;
const int MAXN=4e5+50;
std::vector<int> e[MAXN],Del[MAXN];
int DFN=0,t=0,n,m;
int dfn[MAXN],st[MAXN][21],fa[MAXN];
int pre[MAXN],rt[MAXN],Log[MAXN];
ll dep[MAXN],ans=0;
inline int read()
{
    RG int x=0,f=1; char c=getchar();
    while (c<'0'||c>'9') { if(c=='-') f=-1; c=getchar(); }
    while (c>='0'&&c<='9') { x=(x<<3)+(x<<1)+(c^48); c=getchar(); }
    return x*f;
}
inline void get_dfn(int x,int father)
{
	dfn[x]=++DFN;
	pre[x]=++t; st[t][0]=x;
	fa[x]=father; dep[x]=dep[father]+1;
	for (RG int i=0;i<e[x].size();i++)
	    if (e[x][i]!=fa[x]) get_dfn(e[x][i],x),st[++t][0]=x;
}
inline void get_st()
{
	Log[1]=0;
	for (RG int i=2;i<=t;i++) Log[i]=Log[i>>1]+1;
	for (RG int i=1;i<=Log[t];i++)
	    for (RG int j=1;j<=t-(1<<i)+1;j++) 
	        st[j][i]=dep[st[j][i-1]]<dep[st[j+(1<<(i-1))][i-1]]?st[j][i-1]:st[j+(1<<(i-1))][i-1];
}
inline int get_lca(int x,int y)
{
	x=pre[x],y=pre[y];
	if (x>y) std::swap(x,y);
	RG int k=Log[y-x+1];
	return std::max(1,dep[st[x][k]]<dep[st[y-(1<<k)+1][k]]?st[x][k]:st[y-(1<<k)+1][k]);
}
struct Segment_Tree
{
	int segnum=0;
	struct treenode{int lson,rson,fi,la; ll sum,ans; } tree[MAXN<<4];
	#define lft tree[x].lson
	#define rht tree[x].rson
	inline void up(int x)
	{
	    RG int lca=get_lca(tree[lft].la,tree[rht].fi);
	    //cout<<lft<<" "<<rht<<" "<<lca<<endl;
		tree[x].ans=tree[lft].ans+tree[rht].ans-dep[lca];
		tree[x].fi=tree[lft].fi?tree[lft].fi:tree[rht].fi;
		tree[x].la=tree[rht].la?tree[rht].la:tree[lft].la;
	}
	inline void change(int &x,int p,int v,int L,int R)
	{
		if (!x) x=++segnum;
		if (L==R)
		{
			tree[x].sum+=v;
			tree[x].ans=tree[x].sum?dep[p]:0;
			tree[x].la=tree[x].fi=tree[x].sum?p:0;
			return;
		}
		RG int mid=(L+R)>>1;
		if (dfn[p]<=mid) change(lft,p,v,L,mid);
		else change(rht,p,v,mid+1,R);
		up(x);
	}
	inline ll query(int x)
	{ 
	    RG int lca=get_lca(tree[x].la,tree[x].fi);
	    return tree[x].ans-dep[lca];
	}
	inline void merge(int &x,int v,int L,int R)
	{
		if (!x||!v) { x|=v; return; }
		if (L==R)
		{
			tree[x].sum+=tree[v].sum;
			tree[x].ans|=tree[v].ans;
			tree[x].la|=tree[v].la;
			tree[x].fi|=tree[v].fi;
			return;
		}
		RG int mid=(L+R)>>1;
		merge(lft,tree[v].lson,L,mid);
		merge(rht,tree[v].rson,mid+1,R);
		up(x);
	}
} segment;
inline void solve(int x)
{
	for (RG int i=0;i<e[x].size();i++)
	    if (e[x][i]!=fa[x]) solve(e[x][i]);
	
	for (RG int i=0;i<Del[x].size();i++) segment.change(rt[x],Del[x][i],-1,1,n);
	ans+=segment.query(rt[x]);
	//cout<<ans<<endl;
	if (fa[x]) segment.merge(rt[fa[x]],rt[x],1,n);
}
int main()
{
	//freopen("a.in","r",stdin);
	n=read(),m=read();
	for (RG int i=1;i<n;i++)
	{
		int u=read(),v=read();
		e[u].push_back(v);
		e[v].push_back(u);
	}
	dep[0]=-1;
	get_dfn(1,0);
	get_st();
	for (RG int i=1;i<=m;i++) 
	{
		RG int u=read(),v=read(),lca=get_lca(u,v);
		//cout<<lca<<endl;
		segment.change(rt[u],v,1,1,n); segment.change(rt[v],u,1,1,n); 
		segment.change(rt[v],v,1,1,n); segment.change(rt[u],u,1,1,n); 
		//segment.change(rt[lca],u,-1,1,n); segment.change(rt[lca],v,-1,1,n);
		//if (fa[lca]) segment.change(rt[fa[lca]],u,-1,1,n),segment.change(rt[fa[lca]],v,-1,1,n);
		//cout<<lca<<endl;
		Del[lca].push_back(u),Del[lca].push_back(v);
		if (fa[lca]) Del[fa[lca]].push_back(u),Del[fa[lca]].push_back(v);
	}
	solve(1);
	printf("%lld\n",ans>>1);
	return 0;
}
/*
12 4
1 2
2 3
2 4
4 5
1 6
6 7
6 8
6 9
9 10
9 11
1 12

4 12
8 10
7 11
3 10
*/

还附加了一个小样例,然而样例输出咕了。

标签:P5327,语言,int,线段,dfs,斯坦纳,RG,MAXN,ZJOI2019
来源: https://blog.csdn.net/xmr_pursue_dreams/article/details/100062019