Codeforces Round #720 (Div. 2) D. Nastia Plays with a Tree
最近偶尔回归一下,记录做过的一些题(doge
这题其实就是问最多留下原树中的多少条边,使得没有一个点的度数>2(因为再把所有零散的链连起来就是一整条链了)。这就是一个简单的树上dp,直接做就行。
但坑的是它还要求方案。
好么,我们dp的时候记录一下每个点最优方案中的边都是连向哪的,然后再dfs一遍就可以得到删边之后森林的形状了(说起来容易,写起来吐血)。
最后的最后,我们还需要把每条删的边找到一条对应的新边,这时我们就要在线记录每个联通分量对应的链的两个端点,并且要支持两个联通分量的合并。好么,这不就是并查集吗。
然后我们就做完了哈哈哈!
#include<bits/stdc++.h> #define ll long long using namespace std; const int N=100005; #define pb push_back int f[N][3],n,t,to[N][3],min_pos[N],p[N],ccnt,Side[N][2],color[N],get_fa[N]; vector<int> g[N]; bool ban[N]; inline void init(int x){ for(int i=1;i<=x;i++) g[i].clear(),ban[i] = to[i][0] = to[i][1] = to[i][2] = 0; for(;ccnt;ccnt--) Side[ccnt][0] = Side[ccnt][1] = 0; } int get_pa(int x){ return get_fa[x] == x?x:(get_fa[x] = get_pa(get_fa[x]));} void dfs(int x,int fa){ p[x] = fa; int base = 0,MIN = 1e8,MIN_ADD_SECMIN = 1e9,son = 0; int son_min,son_amin,derta,son_amin_from; for(int s:g[x]) if(s!=fa){ dfs(s,x),son++; son_amin = min(f[s][0],f[s][1]); son_amin_from = f[s][0]<f[s][1] ? 0 : 1; son_min = min(son_amin,f[s][2]); derta = son_amin - son_min; base += son_min; if(MIN + derta < MIN_ADD_SECMIN){ MIN_ADD_SECMIN = MIN + derta; if(derta <= MIN) to[x][2] = to[x][0],to[x][0] = (n+1) * son_amin_from + s; else to[x][2] = (n+1) * son_amin_from + s; } if(derta < MIN) MIN = derta,to[x][1] = (n+1) * son_amin_from + s; } f[x][0] = base + son; f[x][1] = base + MIN + son - 1; f[x][2] = base + MIN_ADD_SECMIN + son - 2; min_pos[x] = 0; if(f[x][1] < f[x][min_pos[x]]) min_pos[x] = 1; if(f[x][2] < f[x][min_pos[x]]) min_pos[x] = 2; } void dfss(int x,int type,const int &ha){ //printf("%d %d\n",x,type); if(type < 0) type = min_pos[x],ban[x] = 1; for(int s:g[x]) if(s!=p[x]){ if(type == 1 && to[x][1] % ha == s) dfss(s,to[x][1] / ha,ha); else if(type == 2){ if(to[x][0] % ha == s) dfss(s,to[x][0] / ha,ha); else if(to[x][2] % ha == s) dfss(s,to[x][2] / ha,ha); else dfss(s,-1,ha); } else dfss(s,-1,ha); } } void dfsss(int x){ color[x] = ccnt; int newson = 0; for(int s:g[x]) if(s!=p[x] && !ban[s]) dfsss(s),newson++; if(newson == 0) if(!Side[ccnt][1]) Side[ccnt][1] = x; else Side[ccnt][0] = Side[ccnt][1],Side[ccnt][1] = x; } inline void output(){ printf("%d\n",min(f[1][0],min(f[1][1],f[1][2]))); for(int i=2,fa,fb;i<=n;i++) if(ban[i]){ fa = get_pa(color[i]),fb = get_pa(color[p[i]]); printf("%d %d %d %d\n",i,p[i],Side[fa][0],Side[fb][1]); get_fa[fb] = fa,Side[fa][0] = Side[fb][0]; } } int main(){ for(scanf("%d",&t);t--;){ init(n),scanf("%d",&n); for(int u,v,i=1;i<n;i++) scanf("%d%d",&u,&v),g[u].pb(v),g[v].pb(u); dfs(1,0); const int ha = n+1; dfss(1,-1,ha); for(int i=1;i<=n;i++) if(ban[i]) ccnt++,Side[ccnt][0] = i,dfsss(i); for(int i=1;i<=ccnt;i++) get_fa[i] = i,Side[i][1] = Side[i][1] == 0 ? Side[i][0]:Side[i][1]; output(); } return 0; }
我爱学习,学习使我快乐