【洛谷3505】[POI2010] TEL-Teleportation(分层思想)

点此看题面

  • 给定一张(n)个点(m)条边的无向图,要求在满足(1)号点到(2)号点至少经过(5)条边的前提下,加入尽可能多的边。
  • (nle4 imes10^4,mle10^6)

分层思想

考虑我们最终肯定是要把图分成(6)层(其中(1)号点在第(0)层,(2)号点在第(5)层),把同层所有点之间以及相邻层每对点之间的边全部连满。

初始与(1)号点有边的肯定全在第(1)层,初始与(2)号点有边的肯定全在第(4)层。

然后我们先证明,一定存在一种最优解,可以不把其他任何一个点放到第(1)层或是第(4)层:以把一个可以在第(2)层的点移到第(1)层为例,原本能与第(1,2,3)层所有点连边,移动后能与第(0,1,2)层所有点连边,因为第(3)层至少有一个点,第(0)层肯定只有(1)号点一个点,所以答案一定不会变优。

于是,剩余的点肯定全在第(2)层或是第(3)层。那么初始与第(1)层的点有边的肯定全在第(2)层,初始与第(4)层的点有边的肯定全在第(3)层。

对于此时还剩下的那些点,考虑第(2)层的点能与第(1,2,3)层所有点连边,第(3)层的点能与第(2,3,4)层所有点连边,只需比较第(1)层和第(4)层的点数即可做出判断,而这是始终固定不变的。

代码:(O(m))

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 40000
#define M 1000000
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
using namespace std;
int n,m,t[6],p[N+5],ee,lnk[N+5];struct edge {int to,nxt;}e[2*M+5];
int main()
{
	RI i,x,y;for(scanf("%d%d",&n,&m),i=1;i<=m;++i) scanf("%d%d",&x,&y),add(x,y),add(y,x);
	for(t[0]=1,i=lnk[1];i;i=e[i].nxt) ++t[p[e[i].to]=1];//与1号点有边在第1层
	for(t[5]=1,i=lnk[2];i;i=e[i].nxt) ++t[p[e[i].to]=4];//与2号点有边在第4层
	for(x=3;x<=n;++x) if(!p[x])//剩余的点
	{
		for(i=lnk[x];i;i=e[i].nxt) if(p[e[i].to]==1) {p[x]=2;break;}else if(p[e[i].to]==4) {p[x]=3;break;}//与第1层有边在第2层,与第4层有边在第3层
		!p[x]&&(p[x]=t[1]>t[4]?2:3),++t[p[x]];//比较1,4两层点数作出选择
	}
	long long s=0;for(i=1;i^5;++i) s+=1LL*t[i]*(t[i]-1)/2;for(i=0;i^5;++i) s+=1LL*t[i]*t[i+1];//同层所有点之间;相邻层每对点之间
	return printf("%lld
",s-m),0;//减去原有边数
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu3505.html