CodeForces 219D Choosing Capital for Treeland

题意

给出一颗有方向的n个节点的树,现在要选择一个点作为首都。
问最少需要翻转多少条边,使得首都可以到所有其他的城市去,以及相应的首都可以是哪些点。

思路

  • 暴力 每个点用dfs求一次需要翻转道路条数显然n情况可行n<=2e5,会超时
  • 求值观察手模发现如果我们假设1求出此时答案ans对于下一个点i,我们可以发现一些规律我们只需要修改原来11到i这条路径即可假设以1为根的树求值时在1到i要修改边数cnt 答案为(i到1路径边数- cnt + ans - cnt 1-i以外修改不变 所以我们只需呀1时候预处理每个路径边数这条边需要修改边数即可
  • (树形dp)dp[i]表示i节点到它的子树所有点最少需要翻转的边。先预处理根节点作为首都需要翻转的边,再精选dp dp[i]表示i作为首都需要翻转的最少边的数量 如果u-v的方向是u-->v,那么dp[v]=dp[u]+1;,否则dp[v]=dp[u]-1。

换根公式:

#include<bits/stdc++.h>
#define PII pair<int,int> 
#define ls u<<1
#define rs u<<1|1
#define xx first
#define yy second
#define rd(x) scanf("%lld",&x)
#define wt(x) printf("%lld\n",x)
#define lowbit(i) i&-i
#define int long long 
using namespace std;
const int N = 2e5+2;
const int mod = 998244353;
int tot,ne[N<<1],head[N<<1],to[N<<1],w[N<<1];
void add(int u,int v,int x)
{
	to[++tot] =v;
	w[tot] = x;
	ne[tot] = head[u];
	head[u] = tot;
}

int dep[N],cnt[N],ans;
void dfs1(int x,int f)
{
	for(int i=head[x];i;i=ne[i])
	{
		int v = to[i] ;
		if(v==f) continue;
		cnt[v] = cnt[x] + (w[i]==0);
		ans += (w[i]==0);
		dep[v] = dep[x] + 1;
		dfs1(v,x);
	}
}

void sol()
{
	int n;
	cin>>n;
	for(int i=1;i<n;i++)
	{
		int u,v;
		cin>>u>>v;
		add(u,v,1);
		add(v,u,0);
	}
	ans=0;
	dfs1(1,0);
	int tot_num = ans;
	vector<int> res;
	res.push_back(1);
	for(int i=2;i<=n;i++)
	{
		int new_num = tot_num + dep[i] - 2*cnt[i];
		if(new_num > ans) continue;
		else if(new_num == ans) res.push_back(i);
		else 
		{
			ans = new_num;
			while(res.size()) res.pop_back();
			res.push_back(i);
		}
	}
	cout<<ans<<endl;
	for(int i=0;i<(int)res.size();i++) cout<<res[i]<<' ';
}
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int t;
	t=1;
//	cin>>t;
	while(t--)
		sol();
	return 0;
}

 树形dp:

#include<bits/stdc++.h>
#define PII pair<int,int> 
#define ls u<<1
#define rs u<<1|1
#define xx first
#define yy second
#define rd(x) scanf("%lld",&x)
#define wt(x) printf("%lld\n",x)
#define lowbit(i) i&-i
#define int long long 
using namespace std;
const int N = 2e5+2;
const int mod = 998244353;
int tot,ne[N<<1],head[N<<1],to[N<<1],w[N<<1];
void add(int u,int v,int x)
{
	to[++tot] =v;
	w[tot] = x;
	ne[tot] = head[u];
	head[u] = tot;
}
int dp[N];
void dfs1(int x,int f)
{
	for(int i=head[x];i;i=ne[i])
	{
		int v = to[i] ;
		if(v==f) continue;
		dfs1(v,x);
		dp[x] += dp[v] + (w[i]==0);
	}
}
void dfs2(int u,int fa)
{
	for(int i=head[u];i;i=ne[i])
	{
		int v=to[i];
		if(v==fa) continue;
		if(w[i]==1) dp[v] = dp[u]+1;
		else dp[v] = dp[u]-1;
		dfs2(v,u);
	}
}
void sol()
{
	int n;
	cin>>n;
	for(int i=1;i<n;i++)
	{
		int u,v;
		cin>>u>>v;
		add(u,v,1);
		add(v,u,0);
	}
	int ans=1e9;
	dfs1(1,0);
	dfs2(1,0);
	vector<int> res;
	for(int i=1;i<=n;i++)
	{
		if(dp[i]<ans)
		{
			ans = dp[i];
			res.clear();
			res.push_back(i);
		}
		else if(dp[i]==ans) res.push_back(i);
	}
	cout<<ans<<endl;
	for(int i=0;i<(int)res.size();i++) cout<<res[i]<<' ';
}
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int t;
	t=1;
//	cin>>t;
	while(t--)
		sol();
	return 0;
}

"educational codeforces round 103 (rated for div. 2)"是一个Codeforces平台上的教育性比赛,专为2级选手设计评级。以下是有关该比赛的回答。 "educational codeforces round 103 (rated for div. 2)"是一场Codeforces平台上的教育性比赛。Codeforces是一个为程序员提供竞赛和评级的在线平台。这场比赛是专为2级选手设计的,这意味着它适合那些在算法和数据结构方面已经积累了一定经验的选手参与。 与其他Codeforces比赛一样,这场比赛将由多个问题组成,选手需要根据给定的问题描述和测试用例,编写程序来解决这些问题。比赛的时限通常有两到三个小时,选手需要在规定的时间内提交他们的解答。他们的程序将在Codeforces的在线评测系统上运行,并根据程序的正确性和效率进行评分。 该比赛被称为"educational",意味着比赛的目的是教育性的,而不是针对专业的竞争性。这种教育性比赛为选手提供了一个学习和提高他们编程技能的机会。即使选手没有在比赛中获得很高的排名,他们也可以从其他选手的解决方案中学习,并通过参与讨论获得更多的知识。 参加"educational codeforces round 103 (rated for div. 2)"对于2级选手来说是很有意义的。他们可以通过解决难度适中的问题来测试和巩固他们的算法和编程技巧。另外,这种比赛对于提高解决问题能力,锻炼思维和提高团队合作能力也是非常有帮助的。 总的来说,"educational codeforces round 103 (rated for div. 2)"是一场为2级选手设计的教育性比赛,旨在提高他们的编程技能和算法能力。参与这样的比赛可以为选手提供学习和进步的机会,同时也促进了编程社区的交流与合作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值