题目描述:你有一棵 (n) 个节点的树,点带权,每次可以砍掉一些边,使得每个联通块的权值和相等,求砍树方案数。
数据范围:(nle 10^6,a_ile 10^9)。
设 (s_x=sum_{i在x子树内} a_i),(S=s_{rt})。
首先假设只有一次操作,要分割成 (k) 部分,那么要么无解要么方案唯一。
按子树权值和从小到大考虑,如果当前子树权值和 (<frac{S}{k}) 则不分割,(=frac{S}{k}) 就要分割,否则无解。
还有一种考虑的方法,我们只能割掉 (frac{S}{k}|s_x) 的 (x) 与其父亲的边,设 (f_k=sum_x[frac{S}{k}|s_x]),则 (f_kle k)。所以有解当且仅当 (f_k=k)。为什么我猜到这个结论还以为是错的啊...
如何求出 (f_k) 呢,则若 (frac{S}{k}|s_i) 时,(s_i) 对 (k) 造成贡献,也就是 (frac{S}{k}|gcd(s_i,S)),即 (frac{S}{gcd(s_i,S)}|k)。所以只需要枚举倍数即可。
设最后一次剖分为 (k) 份时的答案为 (dp_k),则
[dp_k=[f_k=k]sum_{t|k}dp_t
]
总时间复杂度 (O(nlog n))。
#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define PB push_back
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int N = 1000003, mod = 1e9 + 7;
inline int ksm(int a, int b){
int res = 1;
for(;b;b >>= 1, a = (LL) a * a % mod) if(b & 1) res = (LL) res * a % mod;
return res;
}
template<typename T>
inline void read(T &x){
int ch = getchar(); x = 0; bool f = false;
for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
if(f) x = -x;
}
template<typename T>
inline bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
template<typename T>
inline bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
LL gcd(LL a, LL b){return b ? gcd(b, a % b) : a;}
inline void qmo(int &x){x += (x >> 31) & mod;}
int n, fa[N], f[N], dp[N], ans;
LL s[N];
int main(){
read(n);
for(Rint i = 1;i <= n;++ i) read(s[i]);
for(Rint i = 2;i <= n;++ i) read(fa[i]);
for(Rint i = n;i >= 2;-- i) s[fa[i]] += s[i];
for(Rint i = 1;i <= n;++ i){
LL x = s[1] / gcd(s[1], s[i]);
if(x <= n) ++ f[x];
}
for(Rint i = n;i;-- i) if(f[i])
for(Rint j = i << 1;j <= n;j += i) f[j] += f[i];
dp[1] = 1;
for(Rint i = 1;i <= n;++ i) if(f[i] == i){
for(Rint j = i << 1;j <= n;j += i) qmo(dp[j] += dp[i] - mod);
qmo(ans += dp[i] - mod);
}
printf("%d
", ans);
}