CF1554E You

https://www.luogu.com.cn/problem/CF1554E
VP的时候不会做
丢脸……
确实还是有一点难度的

题解

考虑问题可以转换为给每条边定向,然后 a [ u ] a[u] a[u]表示的是 u u u的入度
可以完美的和原问题构成双射,总的方案数就是 2 n − 1 2^{n-1} 2n1
然后发现入度总和是 n − 1 n-1 n1,所以只有 k ∣ ( n − 1 ) k|(n-1) k(n1)的时候才有值
先考虑 K > 1 K>1 K>1
发现值最大为1,因为叶子节点只能指向父亲,然后每个点的入度和必须为K的倍数,那么每条边的方向就定下来了
然后对于 K = 1 K=1 K=1的用总方案数减去即可
code:

#include<bits/stdc++.h>
#define N 200050
#define ll long long
#define mod 998244353
using namespace std;
struct edge {
    int v, nxt;
} e[N << 1];
int p[N], eid;
void init() {
    memset(p, -1, sizeof p);
    eid = 0;
}
void insert(int u, int v) {
    e[eid].v = v;
    e[eid].nxt = p[u];
    p[u] = eid ++;
}
int K, n, a[N], t, ans[N];
void dfs(int u, int fa) {
    for(int i = p[u]; i + 1; i = e[i].nxt) {
        int v = e[i].v;
        if(v == fa) continue;
        dfs(v, u);
    }
    if(a[u] % K == 0) a[fa] ++;
    else a[u] ++;
}
int qpow(ll x, int y) {
    ll ret = 1;
    for(; y; y >>= 1, x = x * x % mod) if(y & 1) ret = ret * x % mod;
    return ret;
}
int gcd(int x, int y) {
    return y? gcd(y, x % y) : x;
}
int check(int k) {
    K = k;
    for(int i = 0; i <= n; i ++) a[i] = 0;
    dfs(1, 0); int d = 0;
    for(int i = 1; i <= n; i ++) d = gcd(d, a[i]);
  //  for(int i = 1; i <= n; i ++) printf("%d ", a[i]); printf("   **%d %d\n", k, d);
    return d == k;
}
int main() {
    scanf("%d", &t);
    while(t --) {
        init();
        scanf("%d", &n);
        for(int i = 1; i <= n; i ++) ans[i] = 0;
        for(int i = 1; i < n; i ++) {
            int u, v;
            scanf("%d%d", &u, &v);
            insert(u, v), insert(v, u);
        }
        for(int k = 1; k * k < n; k ++) if((n - 1) % k == 0){
            if(k > 1) ans[k] = check(k); ans[(n - 1) / k] = check((n - 1) / k);
        }
        ans[1] = qpow(2, n - 1);
        for(int i = 2; i < n; i ++) ans[1] = (ans[1] - ans[i] + mod) % mod;
        for(int i = 1; i <= n; i ++) printf("%d ", ans[i]); printf("\n");
    }
    
    return 0;
}
posted @ 2021-08-06 12:58  lahlah  阅读(31)  评论(0编辑  收藏  举报