codeforces 650E

原题

原题

题目大意

给你两棵树,你要让原树变为新树,每次可以删一条边加一条边,但是在任意时刻,不能出现环,问最小步数以及删边加边的方案。

解题思路

首先考虑那些在初态和终态下都出现的边。这些边显然都是不动的,把它们连接的两个点合并起来。合并时要维护这个集合内的点与集合外的点的连边。

答案很显然为总边数减去不动的边数,即每次删一条原树中的边,并增加一条终态的树上的边。

按照任意顺序删去原树中要改变的边 (u,v) ,然后连一条从 u 所在集合中的点到u所在集合外的点的边,以保证整个图联通。可以保证一定能找到这样的边。(证明?反证,如果没有,那么这个图是不连通的)

参考代码

#include <map>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn=500010;

struct Edge {int x,y;}Ed[maxn];

map <LL,int> M;

int head[maxn],next[maxn<<1],E[maxn<<1],Ecnt;
int Ehead[maxn],Enext[maxn<<1],Etail[maxn<<1],EE[maxn<<1],EEcnt;
int fa[maxn];
int n,m;

void Add_Edge(int x,int y) {
    next[++Ecnt]=head[x];
    head[x]=Ecnt;
    E[Ecnt]=y;
    next[++Ecnt]=head[y];
    head[y]=Ecnt;
    E[Ecnt]=x;
}
void Add_EdgeE(int x,int y,int p) {
    Enext[++EEcnt]=Ehead[x];
    if (Ehead[x]==0) Etail[x]=EEcnt;
    Ehead[x]=EEcnt;
    EE[EEcnt]=p;
    Enext[++EEcnt]=Ehead[y];
    if (Ehead[y]==0) Etail[y]=EEcnt;
    Ehead[y]=EEcnt;
    EE[EEcnt]=p;
}
int Find(int x) {return fa[x]==x?x:fa[x]=Find(fa[x]);}

void DFS(int x,int father)
{
    for (int i=head[x];i;i=next[i]) if (E[i]!=father) DFS(E[i],x);
    if (x==1) return;
    if (Find(x)==Find(father)) return;
    int now=Ehead[Find(x)];
    while (Ehead[Find(x)] && Find(Ed[EE[now]].x)==Find(Ed[EE[now]].y)) 
        now=Ehead[Find(x)]=Enext[Ehead[Find(x)]];
    if (!now) return;
    int u=Ed[EE[now]].x,v=Ed[EE[now]].y;
    printf("%d %d %d %d\n",father,x,u,v);
    Enext[Etail[Find(u)]]=Ehead[Find(v)];
    Etail[Find(u)]=Etail[Find(v)];
    fa[Find(v)]=Find(u);
}

int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++) fa[i]=i;
    for (int i=1,x,y;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        if (x>y) swap(x,y);
        M[(LL)x*n+y]=1;
        Add_Edge(x,y);
    }
    for (int i=1,x,y;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        if (x>y) swap(x,y);
        if (M[(LL)x*n+y]==1) {fa[Find(x)]=Find(y);continue;}
        Ed[++m].x=x,Ed[m].y=y;
    }
    for (int i=1;i<=m;i++) Add_EdgeE(Find(Ed[i].x),Find(Ed[i].y),i);
    printf("%d\n",m);
    DFS(1,0);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值