Codeforces Round #720 (Div. 2) D. Nastia Plays with a Tree
一、算法分析
基本算法是树上统计+贪心。笔者参赛时是不会做的,后来参考了洛谷too_late 的博客 的 https://www.luogu.com.cn/blog/115857/solution-cf1521d题解才码出这道题。下面简单地描述一下思路:
1.划分链的种类,可以将链划分为1号链和2号链。
2.对于每个子树,最好的情况是能保留成1个1号链,或1个二号链。所以对于一棵子树,设子树的根结点为u,u的子结点为集合{v},则对于所有的v,其可能是1号链的根结点(记为1号v)或2号链的根结点(记为2号v)。然后分情况讨论:
(1)只有一个1号v,则带上u作为一个1号链保留(作为主链)。
(2)只有两个1号v,则带上u作为一个2号链保留(作为主链)。
(3)有大于等于3个1号v,则将其中两个作为2号链保留(作为主链),剩下的接在2号链的某一边的下面。
(4)在1~3的情况下存在2号v,则将2号v整体代表的2号链拆下来接在主链上。
(5)只有2号v,选择其中一个2号v作为主子链,将其它的2号v代表的2号链拆下来接到这个主子链的左半边,最后将这个主子链的右半边接到左半边,最后就只剩下一个一号子链,其连接着u结点形成了一个一号链。
总的来说,这道题既考察了思维能力,又考察了代码能力,是一道好题。这种树上统计类型的题目的代码方式也值得学习积累。
二、代码及注释
1 #include<iostream> 2 #include<cstring> 3 #include<algorithm> 4 #include<cstdio> 5 using namespace std; 6 const int N=200050; 7 struct node{ 8 int a,b,c,d; 9 10 }res[N]; 11 int cnt; 12 const int M=N<<1; 13 int h[N],e[M],ne[M],idx; 14 void add(int a,int b){ 15 e[idx]=b,ne[idx]=h[a],h[a]=idx++; 16 } 17 int T; 18 int n; 19 int type[N]; //以i为中心的链是几号链 20 int son[N][2]; //2号链的左右儿子 21 int s[N],t[N]; //以i为中心的链的起点和终点 22 23 void dfs(int u,int fa){ 24 type[u]=1; 25 s[u]=u; 26 t[u]=u; 27 int sons=0; 28 int sum=0; 29 for(int i=h[u];~i;i=ne[i]){ 30 int j=e[i]; 31 if(j==fa) continue; 32 dfs(j,u); 33 sons++; //儿子个数 34 if(type[j]==1) sum++; 35 } 36 if(sum>=2){ //如果多于两条链,就要合并 37 int x=0,y=0; 38 type[u]=2; //合并成就剩一个二号链 39 for(int i=h[u];~i;i=ne[i]){ //选其中两条一号链作为最后保留的二号链 40 int j=e[i]; 41 if(j==fa) continue; 42 if(type[j]==1){ 43 if(!x) x=j; 44 else{ 45 y=x; 46 x=j; 47 break; 48 } 49 } 50 } 51 son[u][0]=x,son[u][1]=y; 52 s[u]=t[y],t[u]=t[x]; 53 for(int i=h[u];~i;i=ne[i]){ 54 int j=e[i]; 55 if(j==fa || j==x || j==y) continue; 56 res[++cnt]={j,u,t[j],t[u]}; //要求第一个和第三个不能相等 57 t[u]=s[j]; 58 } 59 } 60 else if(sum==1){ //只有一个一号链的情况,还要去掉二号链 61 int x=0; 62 for(int i=h[u];~i;i=ne[i]){ 63 int j=e[i]; 64 if(j==fa) continue; 65 if(type[j]==1){ 66 x=j; 67 break; 68 } 69 } 70 t[u]=t[x]; 71 for(int i=h[u];~i;i=ne[i]){ 72 int j=e[i]; 73 if(j==fa ||j==x) continue; 74 //接下来就只剩下二号链了 75 res[++cnt]={j,u,s[j],t[u]}; 76 t[u]=t[j]; 77 } 78 } 79 else if(sons){ //只有二号链的情况,选定一个二号链,然后将其它二号链转移进去 80 int x=0; 81 for(int i=h[u];~i;i=ne[i]){ 82 int j=e[i]; 83 if(j==fa) continue; 84 x=j; 85 break; 86 } 87 for(int i=h[u];~i;i=ne[i]){ 88 int j=e[i]; 89 if(j==fa || j==x) continue; 90 res[++cnt]={j,u,s[j],s[x]}; 91 s[x]=t[j]; 92 } 93 res[++cnt]={son[x][1],x,s[x],t[x]}; //二号链变为一号链 94 t[u]=son[x][1]; 95 } 96 97 } 98 int main(){ 99 100 scanf("%d",&T); 101 while(T--){ 102 memset(h,-1,sizeof(h)); 103 cnt=0; 104 scanf("%d",&n); 105 for(int i=1;i<n;i++){ 106 int a,b; 107 scanf("%d%d",&a,&b); 108 add(a,b); 109 add(b,a); 110 } 111 dfs(1,-1); 112 printf("%d\n",cnt); 113 for(int i=1;i<=cnt;i++) printf("%d %d %d %d\n",res[i].a,res[i].b,res[i].c,res[i].d); 114 } 115 116 117 118 119 return 0; 120 121 }

浙公网安备 33010602011771号