agc033F Adding Edges

agc033F Adding Edges

  • 给你一棵 N N N 个点的树 T T T 和一个 M M M条边的无向图 G G G,对于 G G G进行如下加边操作直到无法操作:
    • 选择 a , b , c a,b,c a,b,c使得 a , b , c a,b,c a,b,c以某种顺序在 T T T的一条链上,并且存在边 ( a , b ) , ( a , c ) (a,b),(a,c) (a,b),(a,c)
    • ( b , c ) (b,c) (b,c)加入 G G G
  • 问最后 G G G中有多少边。
  • N , M ≤ 2000 N,M\le2000 N,M2000

Solution

  • 有一种很妙的转化,如果链上点的顺序为 ( a , b , c ) (a,b,c) (a,b,c),存在 ( a , b ) , ( b , c ) (a,b),(b,c) (a,b),(b,c),那么这是很好做的,只需要从每一点开始dfs即可知道每一个点的连边。
  • 那么如果对于链上顺序的 ( a , b , c ) (a,b,c) (a,b,c),存在 ( a , b ) , ( a , c ) (a,b),(a,c) (a,b),(a,c),我们不妨将 ( a , c ) (a,c) (a,c)换成 ( b , c ) (b,c) (b,c)
  • 记录 f [ x ] [ y ] f[x][y] f[x][y]表示如果有一条边为 ( x , y ) (x,y) (x,y),不妨将它缩短为 ( f [ x ] [ y ] , y ) (f[x][y],y) (f[x][y],y)
  • 新加入一条边就将它缩到最短,然后再考虑加进去后对于那些已经在图里面的边有影响,以 x x x y y y为根dfs即可。然后再不断新加入边。
  • 由于每一个 f [ x ] [ y ] f[x][y] f[x][y]只需要被覆盖一次,所以时空复杂度是 O ( n 2 + n m ) O(n^2+nm) O(n2+nm)的。
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define maxn 2005
using namespace std;

int n,m,i,j,k,f[maxn][maxn],g[maxn][maxn];
int t,w,d[maxn*maxn][2],vis[maxn][maxn];
int em,e[maxn*2],nx[maxn*2],ls[maxn],dep[maxn][maxn];

void insert(int x,int y){
	em++; e[em]=y; nx[em]=ls[x]; ls[x]=em; 
	em++; e[em]=x; nx[em]=ls[y]; ls[y]=em;
}

void dfs(int x,int p,int st){
	dep[st][x]=dep[st][p]+1;
	for(int i=ls[x];i;i=nx[i]) if (e[i]!=p)
		dfs(e[i],x,st);
}

void cover(int x,int p,int st,int v){
	f[st][x]=v;
	if (g[st][x]){
		if (!vis[x][v]) 
			w++,d[w][0]=v,d[w][1]=x,vis[x][v]=vis[v][x]=1;
	}
	for(int i=ls[x];i;i=nx[i]) if (e[i]!=p)
		cover(e[i],x,st,v);
}

void doit(int x,int y){
	g[x][y]=1;
	for(int i=ls[y];i;i=nx[i]) if (dep[x][e[i]]>dep[x][y])	
		cover(e[i],y,x,y);
}

int tot,dfn[maxn],sz[maxn],cnt,E[maxn][2];
void getdfn(int x,int p){
	dfn[x]=++tot,sz[x]=1;
	for(int i=ls[x];i;i=nx[i]) if (e[i]!=p)
		getdfn(e[i],x),sz[x]+=sz[e[i]];
}

int fa[maxn];
int father(int x){return (fa[x]==x)?x:fa[x]=father(fa[x]);}
void link(int x,int y){
	if (father(x)!=father(y))
		fa[fa[x]]=fa[y];
}

int main(){
	freopen("ceshi.in","r",stdin);
	scanf("%d%d",&n,&m);
	for(i=1;i<n;i++) scanf("%d%d",&j,&k),insert(j,k);
	for(i=1;i<=n;i++) dfs(i,0,i);
	while (m--){
		int x,y; scanf("%d%d",&x,&y);
		if (!vis[x][y]) w++,d[w][0]=x,d[w][1]=y,vis[x][y]=vis[y][x]=1;
		while (t<w){
			t++,x=d[t][0],y=d[t][1];
			while (f[x][y]) x=f[x][y];
			while (f[y][x]) y=f[y][x];
			if (!g[x][y]) doit(x,y);
			if (!g[y][x]) doit(y,x);
		}
	}
	for(i=1;i<=n;i++) for(j=i+1;j<=n;j++)
		if (g[i][j]&&!f[i][j]&&!f[j][i])
			cnt++,E[cnt][0]=i,E[cnt][1]=j;
	int ans=0;
	for(int st=1;st<=n;st++){
		tot=0,memset(dfn,0,sizeof(dfn));
		getdfn(st,0);
		for(i=1;i<=n;i++) fa[i]=i;
		for(i=1;i<=cnt;i++) {
			if (dfn[E[i][0]]>dfn[E[i][1]]) swap(E[i][0],E[i][1]);
			if (dfn[E[i][1]]>=dfn[E[i][0]]&&dfn[E[i][1]]<dfn[E[i][0]]+sz[E[i][0]])
				link(E[i][0],E[i][1]);
		}
		for(i=1;i<=n;i++) if (father(i)==father(st)&&i!=st)	
			ans++;
	}
	printf("%d",ans/2);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值