Codeforces Round #389 (Div. 2, Rated, Based on Technocup 2017 - Elimination Round 3) F

来源:互联网 发布:linux查看文件个数命令 编辑:程序博客网 时间:2024/05/16 09:07

题意:

在一片名为treeland的土地上,要举办一次球赛,,这片土地上的n个城市刚好构成一棵树,一共有2k个不同的城市都派出了参赛队伍。首先,要把它们分成k个小组,每个小组刚好两队,每个队只能被分到一组。然后,同一组的两个队会分别在一方的城市进行一场比赛,为此,需要给每组球员安排休息地,每组的休息地要求在该组两个城市最短路径上的一个点。不同组别的休息地可以重叠,要求总的选择的休息地最少。输出最少的休息地数量,以及一个分组方案。 n <= 2E5


solution:

先将这2k个点标记,称作关键点。显然,这棵树上存在一个点,使得以它为根时,所有儿子的子树中,含有关键点数量最大的那个子树的数量不超过k。证明的话,可以先任意找一个点作为目标点,对于它的所有儿子,找到那个含关键点数最多的,如果它的权值小于k,说明找到这个点,否则,目标点往这个儿子调整,其它子树的权值和不超过k,重复执行这种操作,权值最大的那个子树,权值不断减少,总能调整到。

现在找到了这个节点,能证明,在这个点建立休息处,总能找到至少一种配对方案,使得所有路径都经过这个点,只要有办法构造出来就行了。构造的话,每次拿一棵子树,把里面的关键点全部写出来,当所有儿子都操作完以后,就构成一个序列,只要将所有(i,i+k)配对就行了,因为每棵子树的权值不超过k,所以i和i+k一定在两棵不同的子树,既然在不同的子树,就必定经过根

#include<iostream>#include<cstdio>#include<cstring>#include<vector>#include<queue>#include<algorithm>#include<cmath>using namespace std;const int maxn = 2E5 + 20;int n,k,rt,tot,siz[maxn],A[maxn];bool bo[maxn];vector <int> v[maxn];void Dfs1(int x,int fa){siz[x] = bo[x] ? 1 : 0;for (int i = 0; i < v[x].size(); i++){int to = v[x][i];if (to == fa) continue;Dfs1(to,x); siz[x] += siz[to];}} void Dfs2(int x,int fa){bool pass = 0; int Nex;for (int i = 0; i < v[x].size(); i++){int to = v[x][i];if (to == fa) continue;if (siz[to] > k) pass = 1,Nex = to;}if (!pass) rt = x; else Dfs2(Nex,x);}void Dfs3(int x,int fa){if (bo[x]) A[++tot] = x;for (int i = 0; i < v[x].size(); i++){int to = v[x][i];if (to == fa) continue;Dfs3(to,x);}}int main(){#ifdef DMCfreopen("DMC.txt","r",stdin);#endifcin >> n >> k;for (int i = 1; i < n; i++){int x,y; scanf("%d%d",&x,&y);v[x].push_back(y);v[y].push_back(x);}for (int i = 1; i <= 2*k; i++){int x; scanf("%d",&x); bo[x] = 1;}Dfs1(1,0); Dfs2(1,0);if (bo[rt]) A[++tot] = rt;for (int i = 0; i < v[rt].size(); i++)Dfs3(v[rt][i],rt);printf("1\n%d\n",rt);for (int i = 1; i <= k; i++) printf("%d %d %d\n",A[i],A[i+k],rt);return 0;}

0 0
原创粉丝点击