CF650 E

题意:
给出两棵树,要求将第一棵变成第二棵。可以进行的操作是删除一条边,再加入一条边。要求每次操作后仍是一棵树。问最小操作次数和方案。
n<=500000

#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<iostream>
#include<vector>
#define N 510000
#define pb push_back
using namespace std;
struct node{int y,rx,ry,nex;}a[2*N];
vector<int> v1[N],v2[N];
int n,fir[N],len,fa[N],fr[N][2],rt,A[N][4],num;
bool b[N];
void ins(int x,int y,int rx,int ry)
{
    a[++len].y=y;a[len].rx=rx;a[len].ry=ry;a[len].nex=fir[x];fir[x]=len;
}
int find(int x)
{
    if(fa[x]!=x) fa[x]=find(fa[x]);
    return fa[x];
}
void merge(int x,int y)
{
    fa[find(x)]=find(y);
}
void dfs(int x,int fa)
{
    for(int k=fir[x];k;k=a[k].nex)
    {
        int y=a[k].y;
        if(y==fa) continue;
        fr[y][0]=a[k].rx;fr[y][1]=a[k].ry;
        dfs(y,x);
    }
}
void dfs1(int x,int fa)
{
    for(int k=fir[x];k;k=a[k].nex)
    {
        int y=a[k].y;
        if(y==fa) continue;
        A[++num][0]=fr[y][0];A[num][1]=fr[y][1];A[num][2]=a[k].rx;A[num][3]=a[k].ry;
        dfs1(y,x);
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        int x,y;scanf("%d%d",&x,&y);
        v1[x].pb(y);v1[y].pb(x);
    }
    for(int i=1;i<n;i++)
    {
        int x,y;scanf("%d%d",&x,&y);
        v2[x].pb(y);v2[y].pb(x);
    }
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int x=1;x<=n;x++)
    {
        int s1=v1[x].size(),s2=v2[x].size();
        for(int j=0;j<s1;j++) b[v1[x][j]]=1;
        for(int j=0;j<s2;j++) if(b[v2[x][j]]) merge(x,v2[x][j]);
        for(int j=0;j<s1;j++) b[v1[x][j]]=0;
    }
    for(int x=1;x<=n;x++)
    {
        int siz=v1[x].size();
        for(int j=0;j<siz;j++)
        {
            int y=v1[x][j];
            if(find(x)!=find(y)) ins(fa[x],fa[y],x,y);
        }
    }
    rt=fa[1];
    dfs(rt,0);
    len=0;
    for(int i=1;i<=n;i++) fir[i]=0;
    for(int x=1;x<=n;x++)
    {
        int siz=v2[x].size();
        for(int j=0;j<siz;j++)
        {
            int y=v2[x][j];
            if(find(x)!=find(y)) ins(fa[x],fa[y],x,y);
        }
    }
    dfs1(rt,0);
    printf("%d\n",num);
    for(int i=1;i<=num;i++) printf("%d %d %d %d\n",A[i][0],A[i][1],A[i][2],A[i][3]);
}

题解:
先把对的边缩起来,然后以1为根建出两棵树,这个时候所有边都是错的了。
假设现在有n个点,显然答案下界是n-1。
考虑这样构造:
维护两个集合A,B。初始A为{1},B为{2…n}。
A,B时刻满足如下性质
1、A中的点之间的边是对的
2、只考虑A中的边,A内部是联通的
3、B中的点直接联通,或通过与A有关的边间接联通
4、整个图是一棵树
假设向A中加入一个新点x,这个点和已在A中的y在第二棵树中有边。
此时需要删除x的一条边,然后加入(x,y)。
我们在第一棵树上把A中的点标为红色
这里写图片描述
那么容易发现,删除第一棵树上x到父亲的边一定正确。
这样,就构造出了答案的下界。
于是模拟即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值