zoukankan      html  css  js  c++  java
  • AtCoder AGC033F Adding Edges (图论)

    题目链接

    https://atcoder.jp/contests/agc033/tasks/agc033_f

    题解

    又被神仙题搞自闭了……
    首先让我们来读错题:把题面里的"in some order"改成"in this order"! 似乎变简单了很多!
    显然一条边((u,v))会被产生当且仅当在原图上存在从(u)(v)的一条路径,使得这条路径上的点依次位于树上的一条路径上。然后我们可以从每个点开始BFS或DFS确定答案。
    但是现在的题意是"in some order", 没有对路径上三个点的经过顺序进行约束。那么我们考虑把它强行转化成顺序确定的情况。
    假设存在((a,b,c))满足在树上(b)(a,c)的路径上且在图上(a,b)(a,c)之间都有连边,那么把(a,c)的连边去掉,连上(b,c)的边。假设我们这样操作直到不能操作为止,那么新的图产生的边和原图是一样的,且新图加边过程中三个点在树的路径上出现的顺序一定和"in this order"保持一致,然后直接采用刚才的做法做即可。
    现在考虑如何做上面所说的操作。用(f[u][v])维护以(u)为根时如果添加一条((u,v))的边,那么实际上(u)端点会被哪个点替代,然后每次新加一条图的边,先把这条边缩到最短,然后再分别在以(u)为根(v)子树内和以(v)为根(u)子树内找其他能缩的边以及更新(f),详见代码。
    复杂度分析:发现我们每遍历一个点,要么会缩一条边,要么会使得(f)数组里(f[u][v]=u)的个数减少(1), 故总时间复杂度(O(n^2+nm)).

    代码

    #include<bits/stdc++.h>
    #define llong long long
    #define mkpr make_pair
    #define pii pair<int,int>
    #define riterator reverse_iterator
    using namespace std;
    
    inline int read()
    {
    	int x = 0,f = 1; char ch = getchar();
    	for(;!isdigit(ch);ch=getchar()) {if(ch=='-') f = -1;}
    	for(; isdigit(ch);ch=getchar()) {x = x*10+ch-48;}
    	return x*f;
    }
    
    const int N = 2e3;
    struct Edge
    {
    	int v,nxt;
    } e[(N<<1)+3];
    int fe[N+3];
    int fa[N+3][N+3];
    int f[N+3][N+3];
    int n,m,en,ans;
    
    void addedge(int u,int v)
    {
    	en++; e[en].v = v;
    	e[en].nxt = fe[u]; fe[u] = en;
    }
    
    void dfs1(int rt,int u)
    {
    	for(int i=fe[u]; i; i=e[i].nxt)
    	{
    		int v = e[i].v; if(v==fa[rt][u]) continue;
    		fa[rt][v] = u;
    		dfs1(rt,v);
    	}
    }
    
    void addedgeg(int u,int v)
    {
    	if(f[u][v]==v||f[v][u]==u) {return;}
    //	printf("addedgeg %d %d %d %d
    ",u,v,f[u][v],f[v][u]);
    	if(f[u][v]!=u) {addedgeg(f[u][v],v); return;}
    	if(f[v][u]!=v) {addedgeg(u,f[v][u]); return;}
    	f[u][v] = v,f[v][u] = u;
    	vector<pii> ext; queue<int> que;
    	que.push(v);
    	while(!que.empty())
    	{
    		int cu = que.front(); que.pop();
    		for(int i=fe[cu]; i; i=e[i].nxt)
    		{
    			int cv = e[i].v; if(cv==fa[u][cu]) continue;
    			if(f[u][cv]==u) {f[u][cv] = v; que.push(cv);}
    			else {ext.push_back(mkpr(v,cv));}
    		}
    	}
    	que.push(u);
    	while(!que.empty())
    	{
    		int cu = que.front(); que.pop();
    		for(int i=fe[cu]; i; i=e[i].nxt)
    		{
    			int cv = e[i].v; if(cv==fa[v][cu]) continue;
    			if(f[v][cv]==v) {f[v][cv] = u; que.push(cv);}
    			else {ext.push_back(mkpr(u,cv));}
    		}
    	}
    	for(int i=0; i<ext.size(); i++)
    	{
    		addedgeg(ext[i].first,ext[i].second);
    	}
    }
    
    void dfs2(int rt,int u,int prv)
    {
    	if(u!=rt && f[prv][u]==u) {ans++; prv = u;}
    	for(int i=fe[u]; i; i=e[i].nxt)
    	{
    		int v = e[i].v; if(v==fa[rt][u]) continue;
    		dfs2(rt,v,prv);
    	}
    }
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1; i<n; i++) {int u,v; scanf("%d%d",&u,&v); addedge(u,v); addedge(v,u);}
    	for(int i=1; i<=n; i++)
    	{
    		dfs1(i,i);
    	}
    	for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) f[i][j] = i;
    	for(int i=1; i<=m; i++)
    	{
    		int x,y; scanf("%d%d",&x,&y);
    		addedgeg(x,y);
    	}
    	for(int i=1; i<=n; i++)
    	{
    		dfs2(i,i,i);
    	}
    	printf("%d
    ",ans>>1);
    	return 0;
    }
    
  • 相关阅读:
    MongoDB Query 常用方法
    plsql中文乱码问题(显示问号)
    xtype的使用
    LinQ:list基础操作
    VMware Fusion自动切换分辨率
    C#截取字符串的方法小结
    HTML 编码
    AMQP(Advanced Message Queuing Protocol)
    rabibtMQ安装及集群配置linux
    今天是个开始
  • 原文地址:https://www.cnblogs.com/suncongbo/p/12295236.html
Copyright © 2011-2022 走看看