Delegation (USACO Gold 2020 February)

首先只有KKNN的因子的时候结果才有可能是11,否则必须是00

我们指定11号节点为树的根,树上的找可行性/方案的问题,经常需要从子树的角度来看。如果整棵树可以划分成两样长度的链之和,则我们看任何一棵子树uu,会发现与之相关的链只有几种情况:

这样的分类,使得我们想到一个类似tree DP的方法:dfs(u)\text{dfs}(u): 是否有可能将uu子树内部都分成长度为KK的链,最后留下一条uu出发长度K\leq K的链。

对于一个uu,我们首先对所有孩子vv调用dfs(v)\text{dfs}(v),如果有一个返回否,则我们也返回否。如果都是“是”,则剩下的工作,就是要把每个孩子子树中vv出发的链(长度K\leq K)互相配对起来,形成尽量多的KK长的链,这个直接greedy配对就可以了。

复杂度是O(NN的因子数)\mathcal{O}(N \cdot N\text{的因子数})

#include <bits/stdc++.h>
using namespace std;
bool result = true;
int n,k;
vector<int> conn[100005];
int dfs(int pos, int prev) {
    if(result == false) return 0;
    int amn = 0, currtot = 0;
    map<int,int> tot;
 
    for(int i = 0; i < conn[pos].size(); i++) {
        if(conn[pos][i] != prev) {
            int currV = dfs(conn[pos][i],pos) + 1;
            if (result == false) return 0;
            currtot += currV;
            if(currV != k) {
                if(tot.count(k-currV) && tot[k-currV] >= 1) {
                    tot[k-currV]--;
                    amn--;
                }
                else {
                    if (tot.count(currV))
                        tot[currV]++;
                    else
                        tot[currV]=1;
                    amn++;
                }
            }
        }
        if(result == false) return 0;
    }
    if (amn > 1) { result = false; return -1; }
    if (amn == 0) return 0;
    return currtot % k;
}
int main () {
    scanf("%d",&n);
    for(int i = 0; i < n-1; i++) {
        int a,b;
        scanf("%d%d",&a,&b);
        conn[a].push_back(b);
        conn[b].push_back(a);
    }
    printf("1");
    for(int i = 2; i <= n-1; i++) {
        if((n-1)%i == 0) {
            k = i;
            result = true;
            dfs(1,-1);
            printf("%d",result);
        }
        else printf("0");
    }
    printf("\n");
    return 0;
}