[CF]Round513
A Phone Numbers
题意:定义“电话号码”为开头为‘8’,长度为11的字符串。给定一些字符,每个字符只能用一次,求可以拼出多少个电话号码(可以重复)。
直接min(str.length()/11, number_of_8_in(str))
B Maximum Sum of Digits
题意:设\(S(a)\)为\(a\)的各数位之和,求最大的\(S(a)+S(b)\),满足\(a+b=n\)。
让\(a\)全是\(9\)并且尽可能大就行了(鬼知道为什么)。
C Maximum Subrectangle
题意:给定序列\(A,B\),令矩阵\(C\)满足\(C_{i,j} = A_i B_j\),求\(C\)中元素和不超过\(x\)的最大子矩阵\((1\le len(A), len(B), A_i, B_i \le 2000, x \le 2\cdot 10^9)\)。
这个题实际上并不难,令\(f[z]\)为满足\(\sum_{l \le i \le r} b_i \le z\)的最大的\(r-l+1\)。由于\(z\le 2000*2000 = 4\cdot 10^6\),可以暴力枚举区间来更新答案。然后,最终答案就是最大的\((r - l + 1)f[x / \displaystyle\sum_{l \le i\le r} a_i]\)。
D Social Circles
题意:你有一些害♂羞的客人坐在若干个(自定)圆桌旁,他们需要自己左右各有一定数量的椅子,求最少要多少张椅子\((1 \le n \le 10^5)\)。
如果一开始每个人自己坐在一个桌子上,两个人\((a, b), (c, d)\)坐在一起相当于\((a, d), (c, b)\)分开坐。所以我们可以通过交换\(r\)来达到坐在一块的效果,所以,我们让小的\(l\)搭配小的\(r\),就可以算出最优解了。
E Sergey and Subway
题意:给定一棵树,并且连接起距离为2的所有点,求这时的所有点对之间的距离和。
“所有点对之间的距离和”,看到这句话我就想到了以前做过的一个题,直接树形DP就好了,新图中的距离等于\(\lceil dis(i,j)/2\rceil\),但事实上,直接加起来除以2不行的。所以就只能在dfs
的时候顺带计算了,注意到是除以2后上取整,在进入子树的时候有一个奇偶互换的过程,具体实现看代码吧。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
#include <string>
typedef unsigned long long LL;
const int N = 200000+10;
const int M = N<<1;
std::vector<int> g[N];
LL n, tot, ans, sz[N], dep[N], even[N];
void dfs(int x, int f) {
tot += (dep[x] + 1) / 2;
sz[x]++;
even[x] = 1;
for (int i : g[x]) if (i != f) {
dep[i] = dep[x] + 1;
dfs(i, x);
sz[x] += sz[i];
even[f] += even[i];
}
}
void calc(int x, LL to, int f, LL p) {
// p是所有点中到x的距离为偶数的点的个数
// to是当前的答案
ans += to;
for (int i : g[x]) if (i != f) {
// 传递到儿子的时候
// 儿子的子树中的距离儿子为偶数的点的贡献-1
// 儿子的子树外距离儿子为奇数的点的贡献+1
calc(i, to - even[i] + p - sz[i] + even[i], x, n - p);
}
}
int main() {
scanf("%lld", &n);
for (int i = 1, x, y; i < n; ++i) {
scanf("%d%d", &x, &y);
g[x].push_back(y);
g[y].push_back(x);
}
dfs(1, 0);
calc(1, tot, 0, even[1]);
printf("%lld\n", ans / 2);
return 0;
}
emm,实际上,这个题还有一种更加简便的做法:我们完全可以将新图中的距离和写成\((DistSumOfOldTree+NumberOfOddPath)/2\),原图中的距离可以dfs的时候算(按边算贡献,即边的两侧的点数乘积)。然后长度为奇数的路径条数就是深度为奇数的点的个数乘以深度为偶数的点的个数。
F Shrinking Tree
题意:给定一棵树,每次等可能的选择一条边,将其左右端点合并,合并后的新点的编号是合并之前的两个点之一(等可能),问最后剩下的那个点的编号是\(x\)的可能性\((n \le 50)\)。
先推荐一下Mr-Spade的Blog。
这个题结合了树形DP和概率的内容。由于数据极小,即使\(O(n^5)\)的算法卡卡常也能跑过,所以我们不必担心时间复杂度的问题。因此,我们分开算每个点的答案,进行\(n\)次树形DP。
首先定义状态:\(f[i, j]\)表示当根节点的标号传递到\(i\)的时候,\(i\)的子树中还剩下\(j\)条边,只考虑\(i\)的子树,最后能留下来根节点的方案数。这样答案就是\(f[x, n-1] / (n-1)!\)。注意这里的方案数是广义的方案数,即每种方案下留下来的概率的和。
考虑树形DP的两个要素:合并子树的答案和将该节点的答案扩展到父亲。
如何合并子树的答案?考虑左儿子剩\(i\)个,右儿子剩\(j\)个,对父亲剩\(i+j\)个的贡献。由于这\(i+j\)个点删除的时候顺序是无关的,只要保证\(i\)和\(j\)各自的相对顺序就行,所以要乘\({i+j\choose j}\)。同样,之前的边如何删除也是无关的,所以还要乘\({sz(l)+sz(r)-i-j\choose sz(l)-i}\)其中\(sz(i)\)表示边数。
如何扩展到父亲?我们设\(g[i]\)为只考虑一个子树\(v\)时节点\(u\)还剩\(i\)条边的贡献,考虑\(f[v,j]\)的贡献,显然\(j \le i\)。如果\(j < i\),那么就是先删除\(v\)中的边到\(i-1\)条后,根节点标号到达\(u\),删到剩\(j\)条边的时候,根节点标号到达\(v\),由于标号的转移是等概率的,所以要除以\(2\)。当\(j=i\)的时候,也就是在删除\(v\)中的边的过程中已经删除了\(edge(u, v)\),这样一共有\(sz(v) - i + 1\)种删除的位置,所以贡献是\((sz(v)-i+1)*f(v, i)\)
这样,这个题就可做了。(这篇题解大量借鉴Mr-Spade的Blog的思想,代码还是上ta的Blog看吧,我就不献丑了。人类的本质是?)。
G Balls and Pockets
题意:有一个由无限个房间顺次连接构成的区域,每个房间里有一个和房间编号相同的球,其中\(n\)个房间里有一个口袋,定义过滤操作为:将有口袋的房间的球消灭,然后对于每个有球房间,其中的球放入现在编号最小且小于当前房间的空房间,如果没有这样的房间,就不移动这个球。\(m\)次询问第k次过滤后第x个位置上的球的编号\((n,m \le 10^5, x_i, k_i \le 10^9)\)。
H Sophisticated Device
题意:你有无限个存储单元,标号为\(1..\infty\),其中\(1\)号单元存储着数\(x\),二号单元存储着数\(y\),其余单元存储着\(1\),给定如下操作:+ e1 e2 to
表示将单元\(e1\)和\(e2\)中的数的和放入单元\(to\)中,^ e1 e2
表示将单元\(e1\)中的数的\(d\)次方放入\(e2\)中,f e
表示返回单元\(e\)中的值,给定\(d\)和\(p\),你需要设计一个操作序列使得无论\(x,y\)是多少,最后都返回\(xy\)。(以上操作均在模\(p\)意义下,其中\(p\)为质数)\((2 \le d \le 10, 3 \le p \le 10^9 + 9)\)。