题目大意:
大小为 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 1∼n,有多少个不同的序列 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=2n−1
- 对于 f k ( k > 1 ) f_k(k>1) fk(k>1),可以自底向上来做,对于 u u u,假设 v ∈ s o n [ u ] v\in son[u] v∈son[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 (n−1)%k=0,因为 ∑ i = 1 n a i = n − 1 \sum_{i=1}^na_i=n-1 ∑i=1nai=n−1且 a i % k = 0 a_i\%k=0 ai%k=0,所以就存在,那么对于 ( n − 1 ) % k ≠ 0 (n-1)\%k\neq0 (n−1)%k=0,则必然 f k = 0 f_k=0 fk=0
- 所以只要枚举 ( n − 1 ) (n-1) (n−1)的约数进行 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=fk−i=2∑⌊kn⌋hi⋅k
- 时间复杂度为 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(n−1)+n⋅log(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];
}
}