zoukankan      html  css  js  c++  java
  • AGC033F Adding Edges

    一棵树(T)和一张图(G),现在对图进行加边操作:每次找到((a,b,c))满足((a,b),(b,c)in E_G),且(a,b,c)任意顺序(T)上排列在一条链上。

    问对图(G)操作到不能操作时,(|E_G|)是多少。

    (n,mle 2000)


    神仙题。。。对着三个标切的,下次遇到估计还是不会做。。。

    如果(a,b,c)是以固定的顺序(即(a,b,c))排列在一条链上,就可以通过搜索来解决。

    但是这里是以任意顺序,所以尝试调整:如果有((a,c),(a,b)in E_G),且(a,b,c)按顺序排在(T)的一条链上,则将((a,c))替换成((b,c))。(题解把这个操作称作“缩短(shorten)”)。这样调整之后就可以按照上面的方式来搞。

    (f_{u,v})表示如果出现了边((u,v)),就可以将其替换成((f_{u,v},v))。一开始(f_{u,v}=u)

    现在考虑加入一条边((u,v))。先用(f)来将((u,v))缩短到不能再缩为止。在(u)为根的树中,(v)的子树内找到一点(w),如果(f_{u,w}=u),则(f_{u,w}leftarrow v);否则加入边((v,w))(考虑原来有边((u,f_{u,w}),(f_{u,w},w)),现在本来应该有边((u,v),(v,w)),这样形成的相对顺序大概是(u o v o f_{u,w} o w),中间两个的顺序可以调换。这时候当然是要加边((v,f_{u,w})))。在(v)为根的子树中同理。

    以上“加入”和操作的过程用队列实现。

    最后统计时,枚举(u)作为根节点,从(u)开始dfs。记(mid)表示中转点,满足(f_{u,mid}=mid)(记存在调整后的边((u,mid)))。到节点(v)时如果(f_{mid,v}=v)则答案加一并且(midleftarrow v)(反证:如果存在一个中转点(mid')深度比(mid)小,而且(f_{mid',v}=vand f_{mid,v} eq v),因为(f_{mid',mid}=mid,f_{mid',v}=v),所以应有(f_{mid,v}=v),矛盾)。然后继续往下搜。

    时间复杂度的分析大概是说每个(f_{u,v})只会变一次,所以是(O(n^2+nm))


    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    #define N 2005
    int n,m;
    struct EDGE{
    	int to;
    	EDGE *las;
    } e[N*2];
    int ne;
    EDGE *last[N];
    int fa[N][N];
    int rt,mid;
    int f[N][N],bz[N][N];
    queue<pair<int,int> > q;
    void init(int x){
    	for (EDGE *ei=last[x];ei;ei=ei->las)
    		if (ei->to!=fa[rt][x])
    			fa[rt][ei->to]=x,init(ei->to);
    }
    void dfs(int x){
    	f[rt][x]=mid;
    	for (EDGE *ei=last[x];ei;ei=ei->las)
    		if (ei->to!=fa[rt][x]){
    			if (f[rt][ei->to]==rt)
    				dfs(ei->to);
    			else
    				q.push(make_pair(mid,ei->to));
    		}
    }
    int ans;
    void collect(int x,int mid){
    	if (x!=rt && f[mid][x]==x)
    		ans++,mid=x;
    	for (EDGE *ei=last[x];ei;ei=ei->las)
    		if (ei->to!=fa[rt][x])
    			collect(ei->to,mid);
    }
    int main(){
    	freopen("in.txt","r",stdin);
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<n;++i){
    		int u,v;
    		scanf("%d%d",&u,&v);
    		e[ne]={v,last[u]};last[u]=e+ne++;
    		e[ne]={u,last[v]};last[v]=e+ne++;
    	}
    	for (int i=1;i<=n;++i)
    		for (int j=1;j<=n;++j)
    			f[i][j]=i;
    	for (int i=1;i<=n;++i)
    		rt=i,init(rt);
    	for (int i=1;i<=m;++i){
    		int u,v;
    		scanf("%d%d",&u,&v);
    		q.push(make_pair(u,v));
    //		printf("i=%d
    ",i);
    		while (!q.empty()){
    			int u=q.front().first,v=q.front().second;
    //			printf("%d %d
    ",u,v);
    			q.pop();
    			while (u!=v && f[u][v]!=u) u=f[u][v];
    			while (u!=v && f[v][u]!=v) v=f[v][u];
    			if (u==v) continue;
    			rt=u,mid=v,dfs(v);
    			rt=v,mid=u,dfs(u);
    		}
    //		printf("
    ");
    	}
    //	for (int i=1;i<=n;++i)
    //		for (int j=1;j<=n;++j)
    //			if (i!=j && f[i][j]==j)
    //				printf("(%d,%d)
    ",i,j);
    	for (int i=1;i<=n;++i)
    		rt=i,collect(i,i);
    	printf("%d
    ",ans/2);
    	return 0;
    }
    
    
  • 相关阅读:
    KBEngine源码:EntityCall
    skynet 学习笔记-sproto模块(2)
    mongodb:为什么用mongodb
    编写高效服务器程序,需要考虑的因素
    b+树
    mysql:架构
    超越函数/微分方程 /积分中的技术/级数
    积分从入门到放弃<2>
    PyQt4 / PyQt5
    图形学算法:
  • 原文地址:https://www.cnblogs.com/jz-597/p/13866030.html
Copyright © 2011-2022 走看看