ICode9

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

4.18 省选模拟赛 桥 边双联通分量 长链剖分维护贪心

2020-04-24 21:07:58  阅读:223  来源: 互联网

标签:长链 边双 剖分 vis int tn fa MAXN inline


avatar
avatar

只存在加边操作 所以每次只对割边有影响。

考虑求出所有的边双联通分量 然后进行缩点。

那么原图就变成了一颗树 且所有边都是割边。

考虑k==1的时候 显然是求出树的直径。

考虑k>1时 一个错误的贪心:把刚才树的直径上的边标记为0 然后再求直径......

容易构造出反例让其错误。

题解上的做法过于神仙 看不懂。

给一种自己做CF某道题后得到的贪心思路。

容易发现选出k条边 某种意义上来说是选出了2k个点。

我们还可以知道这2k个点中 其中必然有两个点包含树的直径两端点。

但是直接选出2k个点可能是不合法的。而且此时无根很难计算出来答案。

我们知道了必然有两个点是树的直径两端点了 以其中一个点为根。

那么剩下的就是选取2k-1个点每个点的最初权值为点到根路径上的边数。

考虑每次选取最大的。这个贪心容易使用长链剖分进行优化 排一下序或者使用堆维护即可。

正确性:最初根为直径某个端点显然正确,接着这2k个点包括根显然是可以两两配对组成k条边和题目吻合。

最优性:每次权值都尽可能的大所以是最优的。(还需要斟酌一下 好多题都是这样贪的

const int MAXN=200010;
int n,m,Q,id,len=1,cnt,cc,len1=1,s1,s2,maxx;
int c[MAXN],vis[MAXN],dis[MAXN];
int dfn[MAXN],low[MAXN],mark[MAXN<<1],f[MAXN],q[MAXN],mx[MAXN];
int lin[MAXN],ver[MAXN<<1],nex[MAXN<<1],sz[MAXN],son[MAXN],fa1[MAXN];
int lin1[MAXN],ver1[MAXN<<1],nex1[MAXN<<1],e1[MAXN<<1];
inline void add(int x,int y)
{
	ver[++len]=y;
	nex[len]=lin[x];
	lin[x]=len;
}
inline void add1(int x,int y,int z)
{
	ver1[++len1]=y;
	nex1[len1]=lin1[x];
	lin1[x]=len1;
	e1[len1]=z;
}
inline void dfs(int x,int las)
{
	dfn[x]=low[x]=++cnt;
	go(x)
	{
		if(i==(las^1))continue;
		if(!dfn[tn])
		{
			dfs(tn,i);
			low[x]=min(low[x],low[tn]);
			if(low[tn]>dfn[x])mark[i]=1;
		}
		else low[x]=min(low[x],dfn[tn]);
	}
}
inline void dfs(int x)
{
	vis[x]=1;c[x]=id;
	go(x)
	{
		if(vis[tn]||mark[i]||mark[i^1])continue;
		dfs(tn);
	}
}
inline int bfs(int w)
{
	maxx=0;++cc;
	int l=0,r=0,ww=0;
	q[++r]=w;dis[w]=0;vis[w]=cc;
	while(++l<=r)
	{
		int x=q[l];
		if(dis[x]>maxx)
		{
			ww=x;
			maxx=dis[x];
		}
		for(int i=lin1[x];i;i=nex1[i])
		{
			int tn=ver1[i];
			if(vis[tn]!=cc)
			{
				dis[tn]=dis[x]+e1[i];
				vis[tn]=cc;
				q[++r]=tn;
			}
		}
	}
	return ww;
}
inline int dp(int x,int fa)
{
	if(x==s2)return 1;
	for(int i=lin1[x];i;i=nex1[i])
	{
		int tn=ver1[i];
		if(tn==fa)continue;
		if(dp(tn,x))
		{
			e1[i]=e1[i^1]=0;
			return 1;
		}
	}
	return 0;
}
inline int getfather(int x){return x==f[x]?x:f[x]=getfather(f[x]);}
inline void dp1(int x,int fa)
{
	sz[x]=sz[fa]+1;mx[x]=sz[x];
	fa1[x]=fa;
	for(int i=lin1[x];i;i=nex1[i])
	{
		int tn=ver1[i];
		if(tn==fa)continue;
		dp1(tn,x);
		//cout<<mx[tn]<<' '<<mx[x]<<endl;
		if(mx[tn]>mx[x])
		{
			son[x]=tn;
			mx[x]=mx[tn];
		}
	//	cout<<son[x]<<endl;
	}
}
priority_queue<int>s;
inline void dp2(int x,int fa)
{
	if(fa==x)
	{
		if(x==s1)s.push(mx[x]-sz[x]);
		else s.push(mx[x]-sz[fa1[x]]);
		//cout<<mx[x]-sz[x]<<endl;
		//cout<<son[x]<<' '<<x<<endl;
	}
	if(son[x])dp2(son[x],fa);
	for(int i=lin1[x];i;i=nex1[i])
	{
		int tn=ver1[i];
		if(tn==fa1[x]||son[x]==tn)continue;
		dp2(tn,tn);
		//cout<<"ww"<<endl;
	}
}
int main()
{
	//freopen("1.in","r",stdin);
	freopen("bridge.in","r",stdin);
	freopen("bridge.out","w",stdout);
	get(n);get(m);get(Q);
	rep(1,m,i)
	{
		int get(x);int get(y);
		add(x,y);add(y,x);
	}
	dfs(1,0);cc=1;
	rep(1,n,i)if(!vis[i])++id,dfs(i);
	rep(1,id,i)f[i]=i;
	rep(1,n,j)go(j)
	{
		if(c[j]==c[tn])continue;
		int xx=getfather(c[j]);
		int yy=getfather(c[tn]);
		if(xx==yy)continue;
		f[xx]=yy;
		add1(c[j],c[tn],1);
		add1(c[tn],c[j],1);
	}
	--id;int ans=0;
	s1=bfs(1);s2=bfs(s1);
	dp1(s1,0);
	dp2(s1,s1);
	ans+=s.top();
	s.pop();
	put(ans);
	rep(2,Q,i)
	{
		if(s.size())
		{
			ans+=s.top();
			s.pop();
		} 
		if(s.size())
		{
			ans+=s.top();
			s.pop();
		} 
		put(ans);
	}
	return 0;
}

标签:长链,边双,剖分,vis,int,tn,fa,MAXN,inline
来源: https://www.cnblogs.com/chdy/p/12770030.html

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

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

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

ICode9版权所有