[CF1521D]Nastia Plays with a Tree

Nastia Plays with a Tree

题解

简单dp

很明显,我们可以先定义 d p i , j dp_{i,j} dpi,j表示点 i i i在它的子节点中与 j j j个儿子间的边被保留时的子树内最大保留边数。让更改边数最小是等于让保留边数最大的。
对于每个 d p i , j dp_{i,j} dpi,j,我们只需要记录下它当前dp状态与它的儿子之间的连接情况与儿子状态即可。
由于一个点最多只能选择两条边,所以我们只需要找出儿子中选与不选的dp值差距最大的两个即可。
有了连接情况,我们就知道哪些链是需要被保留下来的。将这些链收尾相连即可。
跑一趟dp即可,注意加链时选取的点。

时间复杂度 O ( n ) O\left(n\right) O(n)

源码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 100005
#define lowbit(x) (x&-x)
#define reg register
#define mp make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
const int INF=0x3f3f3f3f;
const int jzm=233;
const int mo=1e9+7;
const double Pi=acos(-1.0);
typedef pair<int,int> pii;
const double PI=acos(-1.0);
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
int n,t,head[MAXN],tot,f[MAXN][3],stak,fa[MAXN],sta[MAXN],pre[MAXN][3],deg[MAXN];
bool link[MAXN][3];
vector<pii> ans;
struct edge{int to,nxt;}e[MAXN<<1];
struct node{int u,v;}s[MAXN];
void addEdge(int u,int v){e[++tot]=(edge){v,head[u]};head[u]=tot;}
void makeSet(int x){for(int i=1;i<=x;i++)fa[i]=i;}
int findSet(int x){return x==fa[x]?x:fa[x]=findSet(fa[x]);}
void unionSet(int a,int b){int u=findSet(a),v=findSet(b);if(u!=v)fa[u]=v;}
void dosaka(int u,int fa){
	int mx1=0,v1=0,mx2=0,v2=0;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;if(v==fa)continue;dosaka(v,u);
		int tmp1=max(f[v][0],f[v][1])+1,tmp2=max(tmp1-1,f[v][2]);
		if(tmp1>tmp2&&tmp1-tmp2>mx1)mx2=mx1,v2=v1,mx1=tmp1-tmp2,v1=v;
		else if(tmp1>tmp2&&tmp1-tmp2>mx2)mx2=tmp1-tmp2,v2=v;
	}
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;if(v==fa)continue;int tmp=max(f[v][0],max(f[v][1],f[v][2]));
		if(v==v1||v==v2)f[u][2]+=max(f[v][0],f[v][1])+1,link[v][2]=1;else f[u][2]+=tmp;
		if(v==v1)f[u][1]+=max(f[v][0],f[v][1])+1,link[v][1]=1;else f[u][1]+=tmp;f[u][0]+=tmp;
		if(v==v1||v==v2)pre[v][2]=f[v][0]>=f[v][1]?0:1;else pre[v][2]=(tmp==f[v][0]?0:(tmp==f[v][1]?1:2));
		if(v==v1)pre[v][1]=f[v][0]>=f[v][1]?0:1;else pre[v][1]=(tmp==f[v][0]?0:(tmp==f[v][1]?1:2));
		pre[v][0]=(tmp==f[v][0]?0:(tmp==f[v][1]?1:2));
	}
}
void dosaka1(int u,int fa,int S){
	if(u==1&&S==1)sta[++stak]=u;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;if(v==fa)continue;
		if(pre[v][S]==1&&!link[v][S])sta[++stak]=v;
		if(!link[v][S])ans.push_back(mp(u,v));
		else unionSet(u,v);
		dosaka1(v,u,pre[v][S]);
	}
	if(!S)sta[++stak]=u;
}
bool cmp(int x,int y){return findSet(x)<findSet(y);}
signed main(){
	read(t);
	while(t--){
		read(n);makeSet(n);
		for(int i=1,u,v;i<n;i++)read(u),read(v),addEdge(u,v),addEdge(v,u),deg[u]++,deg[v]++,s[i]=(node){u,v};dosaka(1,0);
		if(f[1][0]>=f[1][1]&&f[1][0]>=f[1][2])dosaka1(1,0,0);
		else if(f[1][1]>=f[1][2])dosaka1(1,0,1);else dosaka1(1,0,2);
		sort(sta+1,sta+stak+1,cmp);printf("%d\n",ans.size());int id=2;
		for(int i=0;i<(int)ans.size();i++){
			while(id<stak&&findSet(sta[id-1])==findSet(sta[id]))id++;deg[ans[i].fir]--,deg[ans[i].sec]--;deg[sta[id]]++;deg[sta[id-1]]++;
			printf("%d %d %d %d\n",ans[i].fir,ans[i].sec,sta[id-1],sta[id]);id++;
		}
		ans.clear();stak=0;tot=0;
		for(int i=1;i<=n;i++)pre[i][0]=pre[i][1]=pre[i][2]=f[i][0]=f[i][1]=f[i][2]=head[i]=deg[i]=link[i][0]=link[i][1]=link[i][2]=0;
	}
	return 0;
}

谢谢!!!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值