CF1554E(dp)

题目大意:

大小为 n n n的一颗树,可以执行 n n n次操作:取出一个未被选择的点 u u u,看其周围未被选择的点的个数,将数填到 a u a_u au

n n n次操作完之后,有个序列 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an对其求 g c d gcd gcd得到一个数

询问对于 k k k分别为 1 ∼ n 1\sim n 1n,有多少个不同的序列 a a a,使得 g c d ( { a } ) = k gcd(\{a\})=k gcd({a})=k

解题思路:

  • 设序列 { a 1 , a 2 , . . . , a n } \{a_1,a_2,...,a_n\} {a1,a2,...,an}初始都为0,对于一条边 ( u , v ) (u,v) (u,v),有两种选择, a [ u ] + 1 a[u]+1 a[u]+1 a [ v ] + 1 a[v]+1 a[v]+1这样子是能构造出所有可能的序列
  • f i f_i fi为构成序列的 g c d gcd gcd i i i的倍数的个数,那么 f 1 = 2 n − 1 f_1=2^{n-1} f1=2n1
  • 对于 f k ( k > 1 ) f_k(k>1) fk(k>1),可以自底向上来做,对于 u u u,假设 v ∈ s o n [ u ] v\in son[u] vson[u]已经处理完, a [ v ] % k = 0 a[v]\%k=0 a[v]%k=0,那么对于未被选择的边都必须加到 a [ u ] a[u] a[u]上,倘若加到 a [ v ] a[v] a[v]上,那么必然 ( a [ v ] + 1 ) % k = 1 (a[v]+1)\%k=1 (a[v]+1)%k=1,则显然不合法,而如果$
  • 对于 a [ u ] a[u] a[u]与其父亲连的边 p [ u ] p[u] p[u],倘若 a [ u ] % k = 0 a[u]\%k=0 a[u]%k=0,那么这条边就不会贡献到 a [ u ] a[u] a[u]上,倘若 a [ u ] % k ≠ 0 a[u]\%k\neq 0 a[u]%k=0那么这条边必须贡献给 a [ u ] a[u] a[u],如果 ( a [ u ] + 1 ) % k ≠ 0 (a[u]+1)\%k \neq 0 (a[u]+1)%k=0那么则可以终止,说明 f k = 0 f_k=0 fk=0
  • 通过上述可以发现 f k = 0   o r   1 f_k=0\, or\, 1 fk=0or1
  • 但这样复杂度是 O ( n 2 ) O(n^2) O(n2)的显然不能接受
  • 可以发现倘若 f k = 1 f_k=1 fk=1,那么必然存在 ( n − 1 ) % k = 0 (n-1)\%k=0 (n1)%k=0,因为 ∑ i = 1 n a i = n − 1 \sum_{i=1}^na_i=n-1 i=1nai=n1 a i % k = 0 a_i\%k=0 ai%k=0,所以就存在,那么对于 ( n − 1 ) % k ≠ 0 (n-1)\%k\neq0 (n1)%k=0,则必然 f k = 0 f_k=0 fk=0
  • 所以只要枚举 ( n − 1 ) (n-1) (n1)的约数进行 d f s dfs dfs即可
  • h k h_k hk为题目要求的,则 h k = f k − ∑ i = 2 ⌊ n k ⌋ h i ⋅ k h_k = f_k - \sum\limits_{i = 2}^{\left \lfloor{\frac{n}{k}}\right \rfloor}{h_{i \cdot k}} hk=fki=2knhik
  • 时间复杂度为 O ( n ⋅ σ 0 ( n − 1 ) + n ⋅ l o g ( n ) ) \mathcal{O}(n \cdot \sigma_0 (n - 1) + n \cdot log(n)) O(nσ0(n1)+nlog(n))
  • 感觉难点主要在问题转换上…

AC代码:

#include <bits/stdc++.h>
#define ft first
#define sd second
#define pb push_back
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) //不能跟puts混用
#define seteps(N) fixed << setprecision(N)
#define endl "\n"
const int maxn = 1e5 + 10;
const int mod = 998244353;
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int, int> pii;
ll qpow(ll a, ll b, ll mod) {
   ll res = 1;
   while (b) {
       if (b & 1) res = res * a % mod;
       a = a * a % mod;
       b >>= 1;
   }
   return res;
}
int t, n, f[maxn], h[maxn], a[maxn];
bool vis[maxn];
vector <pii> G[maxn];
vector <int> divs;
void init() {
    for (int i = 1; i <= n; i++) G[i].clear(), f[i] = h[i] = 0;
    divs.clear();
    for (int i = 2; i <= n - 1; i++) {
        if ((n - 1) % i) continue;
        divs.pb(i);
    }
}
void dfs(int u, int fu, int k, int fu_u_id, bool &ok) {
    if (!ok) return;
    for (auto np : G[u]) {
        if (np.ft == fu) continue;
        dfs(np.ft, u, k, np.sd, ok);
    }
    for (auto np : G[u]) {
        if (np.ft == fu || vis[np.sd]) continue;
        vis[np.sd] = 1;
        a[u]++;
    }
    if (fu_u_id) {
        if (a[u] % k == 0) return;
        else if ((a[u] + 1) % k == 0 && !vis[fu_u_id]) vis[fu_u_id] = true, a[u]++;
        else ok = false;
    }
    else if (a[u] % k) ok = false;
    return;
}
int main() {
    IOS;
    cin >> t;
    while (t--) {
        cin >> n;
        init();
        for (int i = 1, u, v; i < n; i++) {
            cin >> u >> v;
            G[u].pb({v, i}), G[v].pb({u, i});
        }
        for (int k : divs) {
            bool ok = true;
            memset(vis, 0, sizeof(bool) * (n + 1));
            memset(a, 0, sizeof(int) * (n + 1));
            dfs(1, 0, k, 0, ok);
            if (ok) f[k] = 1;
        }
        f[1] = qpow(2, n - 1, mod);
        for (int k = n; k >= 1; k--) {
            h[k] = f[k];
            for (int i = 2; i <= n / k; i++) h[k] -= h[i * k];
        }
        for (int i = 1; i <= n; i++)
            cout << h[i] << " \n"[i == n];
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值