题解 P6147 [USACO20FEB]Delegation G

求对所有 \(k \in [1, n-1]\) 能不能把树分成若干条长 \(k\) 的链。

《赛道修建》没学透啊,拼个链都不记得了。

NOIP2018 赛道修建中的拼链方法

求得每个儿子剩下来的链长度(从下往上跨过一个点的链最多一条)后用双指针拼上。

具体地:

  1. 递归求解每个儿子生下来的长度并记录。
  2. 排序。
  3. 若最左和最右能恰好拼成则两个指针向中间移动。
  4. 若不能则记录剩下的并移动相应指针。
  5. 若有多个剩下就失败了。

这样直接做的复杂度是 \(O(n^2 \log n)\) 的。
这里只有约数可能可以,判一下再跑居然过了。

代码
#include <iostream>
#include <vector>
#include <algorithm>
const int N = 100005;
int n, x, y;
std::vector<int> g[N];
int dfs(int u, int fa, int len) {
    std::vector<int> a;
    for (int i = 0; i < (int)g[u].size(); i++) {
        int v = g[u][i];
        if (v == fa) continue;
        int t = dfs(v, u, len);
        if (t == -1) return -1;
        if (t + 1 == len) continue;
        a.push_back(t+1);
    }
    std::sort(a.begin(), a.end());
    int l = 0, r = a.size()-1, an = 0;
    while (l < r) {
        if (a[l] + a[r] == len) l ++, r --;
        else if (an) return -1;
        else if (a[l] + a[r] > len) an = a[r], r --;
        else an = a[l], l ++;
    }
    if (l > r) return an;
    else if (an) return -1;
    return a[l];
}
int main() {
    std::cin >> n;
    if (n == 1) return 0;
    for (int i = 1; i < n; i++) {
        std::cin >> x >> y;
        g[x].push_back(y), g[y].push_back(x);
    }
    std::cout << '1';
    for (int i = 2; i < n; i++) 
        if ((n-1) % i == 0 && dfs(1, 0, i) == 0) std::cout << '1';
        else std::cout << '0';
}
posted @ 2021-08-07 16:23  Acfboy  阅读(26)  评论(0编辑  收藏  举报