最近偶尔回归一下,记录做过的一些题(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
",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
",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
",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;
}