1. 程式人生 > 其它 >題解 P6147 [USACO20FEB]Delegation G

題解 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';
}