Region Separation (CF1034C)
Region Separation(CF1034C)
明确题意:
1.每次砍完之后,所有联通快的权值和相等。
2.每次一个边集的边同时断,可以断多次边集。
题解:
和学长馈赠7类似,是加强版。
首先可以想到计算出每个子树的权值和——\(siz\) (以 \(1\) 为根)。
设切到最后每个联通块的权值和为 \(k\) ,那么合法的 \(k\) 满足 \(k|siz_1\) 。
记 \(cnt_k=\sum_{i=1}^n{[siz_i \equiv 0 (mod \ k)]}\) ,合法的 \(k\) 就还要满足 \(cnt_k=\frac{siz_1}{k}\) 。
证明:
\(cnt_k\) 实际上是满足 \(siz_i \equiv 0 (mod \ k)\) 的节点个数。
这些节点要选择断开与父节点相连的边,如果我们没有断开 \(\frac{siz_1}{k}-1\) 条边(考虑到根节点不用断,实际上断开了 \(\frac{siz_1}{k}-1\) 条边),也就没有把这棵树分成 \(\frac{siz_1}{k}\) 个部分,那不可能每个部分的权值和为 \(k\) 。
\(\text{Q.E.D.}\)
但是还有一个问题:\(k\) 的范围达到了 \(10^6\times 10^9\) ,开桶存完全是不现实的。
于是考虑转变一下 \(cnt\) 的定义。
新定义:\(cnt_s\) 为满足最后剩下了 \(s\) 个联通块,要断开与祖先相连的边的点的个数。(可能说不太清楚,看式子吧,实际上是 \(s=\frac{siz_1}{k}\),代换了一下,这样桶存的范围变成了 \(10^6\) )
既要满足:
合法条件是 \(cnt_s=s\) 。
计算 \(cnt\) :
暴力枚举 \(s\) 、 \(i\) 复杂度为 \(O(n^2)\) 。
发现:对于一颗权值和为 \(siz_i\) 的子树来说,它如果能对 \(s\) 做贡献,要满足:
设 \(d=\gcd(siz_1,siz_i)\) ,\(siz_1=a\times d\) ,\(siz_i=b\times d\) ,则 \(gcd(a,b)=1\) 。
所以
发现:当 \(b|t\) 是,\(s\) 才为整数,即对 \(s\) 有贡献,所以 \(s=g\times a(g=\frac{t}{b})\) 。
得出结论:当 \(\frac{siz_1}{\gcd(siz_1,siz_i)}|s\) 时, \(siz_i\) 会在 \(cnt_s\) 中做贡献。
这里的计算是枚举 \(\frac{siz_1}{\gcd(siz_1,siz_i)}\) 的倍数,调和级数,复杂度 \(O(n \ln n)\) 。
这只是第一步:判断那些状态合法,接下来还要计算方案数。
设 \(f_s\) 为最后剩下了 \(s\) 个联通块的方案数。
初始化:\(f_s=[cnt_s = s]\) 。
考虑转移:\(f_s=\sum_{s|s_1}f_{s_1}\) (切一下成倍增加)。
注意:\(f_1=1\) 需要特殊判一下。
最后这部分时间复杂度 \(O(n \ln n)\) 。
Code moo~~
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define re register int
#define pc_ putchar(' ')
#define pc_n putchar('\n')
#define Bessie signed
const int CTR = 1e6 + 7, MOD = 1e9 + 7;
int n;
int a[CTR], fa[CTR], siz[CTR];
int cnt[CTR], f[CTR];
int ans;
int gcd(int x, int y)
{
return !y ? x : gcd(y, x % y);
}
Bessie main()
{
n = read();
for(re i = 1; i <= n; ++i)
a[i] = read();
for(re i = 2; i <= n; ++i)
fa[i] = read();
for(re i = n; i; --i)
{
siz[i] += a[i];
siz[fa[i]] += siz[i];
}
for(re i = 1; i <= n; ++i)
if(siz[1] / gcd(siz[1], siz[i]) <= n)
++cnt[siz[1] / gcd(siz[1], siz[i])];
for(re i = 1; i <= n; ++i)
if(cnt[i])
for(re j = i; j <= n; j += i)
f[j] += cnt[i];
for(re i = 1; i <= n; ++i)
f[i] = (f[i] == i);
for(re i = n; i > 1; --i)
{
if(f[i])
{
for(re j = i + i; j <= n; j += i)
(f[i] += f[j]) %= MOD;
(ans += f[i]) %= MOD;
}
}
ot(ans + 1),pc_n;
return 0;
}