ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

P5327 [ZJOI2019]语言

2019-08-25 13:45:56  阅读:280  来源: 互联网

标签:P5327 语言 int 线段 dfs 斯坦纳 RG MAXN 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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有