CF650 E

来源:互联网 发布:c语言中int什么意思 编辑:程序博客网 时间:2024/06/01 09:22

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

#include<cstring>#include<cstdlib>#include<cstdio>#include<cmath>#include<iostream>#include<vector>#define N 510000#define pb push_backusing 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到父亲的边一定正确。
这样,就构造出了答案的下界。
于是模拟即可。

0 0