题意

一棵 \(n\) 个点的树,每个点有权值 \(a_i\) 。你想砍树。

你可以砍任意次,每次你选择一些边断开,需要满足砍完后每个连通块的权值和是相等的。求有多少种砍树方案。

\(n \le 10^6, a_i \le 10^9\)

题解

先假设只砍一次。令所有点权和为 \(S\) ,那么假设要砍成 \(k\) 个连通块,则每个连通块的权值和均为 \(\displaystyle \frac{S}{k}\) 。

考虑如何得到砍的方案,以 \(1\) 号点为根 \(dfs\) ,若当前点 \(i\) 的子树之和 \(\frac{S}{k} | \displaystyle sum_i\) ,则当前子树可以砍下来。若最后恰好砍了 \(k\) 次,那么就得到了一个合法的砍树方案。

其实这就等价于 \(\displaystyle \sum_{i=1}^{n} [\frac{S}{k} | sum_i] = k\) 。

不难看出这个对应且仅对应一种方案。如果不足 \(k\) ,那么就没有那么多个点可以分;多于 \(k\) 的情况是不可能的,因为总和不够分配。

这个式子还不够优秀,我们转化一下:

\[\begin{align}
[\frac{S}{k}|sum_i] &= [S | k \times sum_i] \\
&= [\frac{S}{\gcd(S,sum_i)}|k \times \frac{sum_i}{\gcd(S,sum_i)}] \\
&\because \frac{S}{\gcd(S,sum_i)} \bot \frac{sum_i}{\gcd(S,sum_i)} \\
&= [\frac{S}{\gcd(S,sum_i)} | k]
\end{align}
\]

然后就变成

\[\sum_{i = 1}^{n} [\frac{S}{\gcd(S,sum_i)} | k] = k
\]

显然这个我们可以枚举倍数在 \(O(n \ln n)\) 的时间内解决(注意 \(k \le n\) )

那么如果砍多次呢?可以看出如果第一次砍成了 \(x\) 块,那么第二次砍成的块数 \(y\) 必须满足 \(x|y\) 。

因为你之后的权值只能比之前分的更多,且每个联通块的权值是之前的一个因子。

这部分也可以 \(O(n \ln n)\) 算。

总结

熟悉这种分成很多块有关于 \(O(\ln n)\) 复杂度的东西就行啦qwq

代码

#include <bits/stdc++.h>

#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << (x) << endl
#define DEBUG(...) fprintf(stderr, __VA_ARGS__) using namespace std; typedef long long ll; template<typename T> inline bool chkmin(T &a, T b) {return b < a ? a = b, 1 : 0;}
template<typename T> inline bool chkmax(T &a, T b) {return b > a ? a = b, 1 : 0;} inline int read() {
int x(0), sgn(1); char ch(getchar());
for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
return x * sgn;
} void File() {
#ifdef zjp_shadow
freopen ("C.in", "r", stdin);
freopen ("C.out", "w", stdout);
#endif
} const int N = 1e6 + 1e3; bitset<N> pass; ll sum[N], dp[N]; int n, fa[N]; int main () { File(); n = read();
For (i, 1, n) sum[i] = read();
For (i, 2, n) fa[i] = read();
Fordown (i, n, 1) sum[fa[i]] += sum[i]; For (i, 1, n) {
ll tmp = sum[1] / __gcd(sum[1], sum[i]);
if (tmp <= n) ++ dp[tmp];
} Fordown (i, n, 1) if (dp[i])
for (int j = i * 2; j <= n; j += i) dp[j] += dp[i]; For (i, 1, n)
pass[i] = (dp[i] == i && !(sum[1] % i)), dp[i] = 0;
dp[1] = pass[1]; ll ans = 0;
For (i, 1, n) if (pass[i]) {
for (int j = i * 2; j <= n; j += i)
if (pass[j]) dp[j] += dp[i];
ans += dp[i];
}
printf ("%lld\n", ans); return 0; }

Codeforces Round #511 (Div. 1) C. Region Separation(dp + 数论)的更多相关文章

  1. Codeforces Round #511 (Div. 2)

    Codeforces Round #511 (Div. 2) #include <bits/stdc++.h> using namespace std; int n; int main() ...

  2. 2018.9.21 Codeforces Round #511(Div.2)

    只写了AB,甚至还WA了一次A题,暴露了蒟蒻的本质=.= 感觉考的时候有好多正确或和正解有关的思路,但是就想不出具体的解法或者想的不够深(长)(怕不是过于鶸) 话说CF的E题怎么都这么清奇=.= A. ...

  3. Codeforces Round #174 (Div. 1) B. Cow Program(dp + 记忆化)

    题目链接:http://codeforces.com/contest/283/problem/B 思路: dp[now][flag]表示现在在位置now,flag表示是接下来要做的步骤,然后根据题意记 ...

  4. Codeforces Round #511 (Div. 2):C. Enlarge GCD(数学)

    C. Enlarge GCD 题目链接:https://codeforces.com/contest/1047/problem/C 题意: 给出n个数,然后你可以移除一些数.现在要求你移除最少的数,让 ...

  5. Codeforces Round #448 (Div. 2) A. Pizza Separation【前缀和/枚举/将圆(披萨)分为连续的两块使其差最小】

    A. Pizza Separation time limit per test 1 second memory limit per test 256 megabytes input standard ...

  6. Codeforces Round #511 (Div. 2)-C - Enlarge GCD (素数筛)

    传送门:http://codeforces.com/contest/1047/problem/C 题意: 给定n个数,问最少要去掉几个数,使得剩下的数gcd 大于原来n个数的gcd值. 思路: 自己一 ...

  7. Codeforces Round #511 Div.1 A Div.2 C

    嗯切一题走人很开心. gzy-50分比我还惨. 题意:有n个数,去掉尽量少的数使得剩下数的gcd变大. 首先把这n个数都除以gcd,就变成了去掉尽量少的数使得gcd不等于1. 可以枚举一个质数,然后统 ...

  8. C. Enlarge GCD Codeforces Round #511 (Div. 2)【数学】

    题目: Mr. F has nn positive integers, a1,a2,…,an. He thinks the greatest common divisor of these integ ...

  9. B. Cover Points Codeforces Round #511 (Div. 2)【数学】

    题目: There are nn points on the plane, (x1,y1),(x2,y2),…,(xn,yn)(x1,y1),(x2,y2),…,(xn,yn). You need t ...

随机推荐

  1. 16-使用Selenium模拟浏览器抓取淘宝商品美食信息

    淘宝由于含有很多请求参数和加密参数,如果直接分析ajax会非常繁琐,selenium自动化测试工具可以驱动浏览器自动完成一些操作,如模拟点击.输入.下拉等,这样我们只需要关心操作而不需要关心后台发生了 ...

  2. Git远程分支的回退

    下午发现上午提交的一个版本有问题,在回退本地分支后,发现还必须要回退远程分支的版本.网上查找到的资料如下: #新建old_master分支做备份 git branch old_master #push ...

  3. 社交CRM SCRM

    社交CRM - 国际版 Binghttps://cn.bing.com/search?FORM=U227DF&PC=U227&q=%E7%A4%BE%E4%BA%A4CRM 社交CRM ...

  4. [转][mysql]创建函数失败(1418错误)mysql双主模式导致的问题

    https://blog.csdn.net/qq523786283/article/details/75102170

  5. java线程池实现原理

    (1):线程池存在哪些状态,这些状态之间是如何进行切换的呢? (2):线程池的种类有哪些? (3):创建线程池需要哪些参数,这些参数的具体含义是什么? (4):将任务添加到线程池之后运行流程? (5) ...

  6. [转帖]Linux 下如何知道是否有人在使坏?

    Linux 下如何知道是否有人在使坏? 学到了两个最简单的命令 usermod -L username 锁定账户 passwd -s username 查看用户状态. 自己linux 知道的还是少 需 ...

  7. java随笔2 变量类定义

    如果要定义变量为对象,就要创建此对象对应的java类, 且定义的类型为类名,且都为private

  8. Day 4-6 xml处理

    xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单,不过,古时候,在json还没诞生的黑暗年代,大家只能选择用xml呀,至今很多传统公司如金融行业的很多系统的 ...

  9. UTF-8编码与GBK编码下的字符长度

    源码: package lsh.java.charset; import java.nio.charset.Charset; public class LengthOfUTF_8 { public s ...

  10. Golang的Json encode/decode以及[]byte和string的转换

    使用了太长时间的python,对于强类型的Golang适应起来稍微有点费力,不过操作一次之后发现,只有这么严格的类型规定,才能让数据尽量减少在传输和解析过程中的错误.我尝试使用Golang创建了一个公 ...