CodeForces - 748F

Santa Clauses and a Soccer Championship

Point

settle them in the minimum possible number of cities m in such a way

掉入了出题的陷阱当中,这道题目只需要一个休息站就可以了,具体分析见背景

权值最短路与多个休息点两个信息,起到了很好的误导作用。思路一直被限制在图论特别是Floyd上面

BackGround

The country Treeland consists of n cities connected with n - 1 bidirectional roads in such a way that it's possible to reach every city starting from any other city using these roads.

n个城市,由n-1条道路连通,类似于最小生成树?这说明没有多余的道路,每一条道路都至关重要(即,如果去掉一条道路,必然至少存在一个City无法与Other Cities 相连通),也就是从City A 到 City B只有一种走法。

又因为只要休息点安排在City A 与 City B的必由之路上,由于一来一回的两场比赛,那么,必然是最短路

鉴于以上两点,我们可以轻易地得出:所求的m个点是所有道路的交点

现在来论证(胡扯)m必定为1:题目中只是给出了k对的Team,但是没有指定具体的对阵名单,自然我们就可以随意指定了。

由题设可知,必然存在一个City,以此City作为分割,能够把Team分成两个部分,这两个部分的Team个数分别为k(若用以分割的City本身是含有一个Team,可把这个City归入其中某一个部分,则结论依然成立)。
这样,选中的City就成为了沟通两个部分不可或缺的“桥梁”,通过对分属“桥梁”两边的Team作一一映射,我们就完成了证明,且这时休息点,即为“桥梁”。

Algorithm

依照上面的思路,我们可以完成算法设计,但仍需要解决两个问题:
1、哪个点可以作为分割点?
2、当分割点存在多个分支道路的时候,我们应该把哪几条道路划分成一个部分,而把其它道路划分成另一部分呢?

秉承暴力解决是唯一真解的理念,再配合一点小聪明,就不必完全解决这两个问题,只需要假装部分完成,然后再说服自己就可以了。

回到问题本身,我们为什么要把Team分成两个均为k的部分?因为一一对应;那么,如果某一部分超过了k就会出现,内部解决,内部消化的问题,桥梁也无法构成“必由之路”,增加了m的大小。

对于大的整体部分是这样的,小的单条分支也都是这样,超过k这个分割点就是不成立的
也就是说我们只需要计算每个City 每条分支的上的Team个数,若超过k则不成立,<=k就符合条件

到此为止我们已经非常成功地解决了寻找分割点的问题,那么道路分配问题应该怎么解决呢?
其实,并不必解决,我们只需要对找到的分割点进行DFS处理即可,把前面k个点丢给Part I 后面 k个点丢个Part II,按照DFS的顺序一一对应就可以,这样的分割虽然与我们最开始提出的有所不同,但是同样可以起到很好的效果

Code

    #define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>

using namespace std;
const int maxn = 2e5 + 10;
vector<int> G[maxn];
int val[maxn];
int val_all_sons[maxn];
int n, k;
queue<int> q1, q2;

int dfs(int u, int fa)
{
    int & use = val_all_sons[u];
    int ans = 0;

    for (int i = 0; i < G[u].size(); i++)
    {
        int v = G[u][i];
        if (v == fa) continue;
        int tem = dfs(v, u);
        use = max(use, tem);
        ans += tem;
    }
    use = max(use, 2 * k - ans);
    return ans + val[u];
}

void dfs_city(int u, int fa)
{
    if (val[u])
    {
        if (q1.size() < k)
            q1.push(u);
        else
            q2.push(u);
    }
    for (int i = 0; i < G[u].size(); i++)
    {
        int v = G[u][i];
        if (v == fa) continue;
        dfs_city(v, u);
    }
}

int main()
{
    scanf("%d%d", &n, &k);
    int u, v;
    for (int i = 0; i < n - 1; i++)
    {
        scanf("%d%d", &u, &v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    int a;
    for (int i = 0; i < 2 * k; i++)
        scanf("%d", &a), val[a] = 1;
    dfs(1, 0);
    int out;
    //if (k == 1) out = a;
    for (int i = 1; i <= n; i++)
        if (val_all_sons[i] <= k) out = i;

    printf("1\n%d\n", out);
    dfs_city(out, 0);
    if (q2.size()) q2.push(out);
    while (q1.size())
    {
        int c1, c2;
        c1 = q1.front(), c2 = q2.front();
        printf("%d %d %d\n", c1, c2, out);
        q1.pop(), q2.pop();
    }

    return 0;
}

Note

无聊胡乱猜想,题目中给的是k Pair 而不是 2k Team 是不是在暗示什么呢?

Thanks For Watching

—END—

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值