AtCoder Regular Contest 116 补题(C、D、E)
C - Multiple Sequences
题意:
给定数字\(N\)和\(M\),问有多少个长度为\(N\)的序列,满足\(1≤A_i≤M\),且\(A_{i+1}\)是\(A_{i}\)的倍数
思路:
由于序列是成倍变大的,所以序列肯定是一个非严格单调递增的序列,那么可以枚举最后一个数\(A_n\),通过递推可知,前面的数都是\(A_n\)的约数,所以就相当于将\(A_n\)的质因数拆解后,往前面\(n-1\)个空放,对于每个因子来说都是独立的,所以质因数分解后,当前这个因子就是\(p_i^{c_i}\),当前因子为\(c_i\),由于因子是可不选的,所以相当于有\(n\)个空(\(1\)个是用来代表空放的),有\(c\)个球,所以放的方案数就是\({n+c-1\choose{n-1}}\),对于当前枚举的数,\(\prod \limits_{i=1}^{n}{n+c_i-1\choose{n-1}}\),然后把所有枚举的情况累加即答案
View Code
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 4e5 + 10;
const int mod = 998244353;
int fact[N], infact[N];
int n, m;
int lowbit(int x) { return x & -x; }
int qmi(int a, int k) {
int res = 1;
while (k) {
if (k & 1) res = res * a % mod;
k >>= 1;
a = a * a % mod;
}
return res;
}
void init() {
fact[0] = infact[0] = 1;
for (int i = 1; i < N; i++) {
fact[i] = fact[i - 1] * i % mod;
}
infact[N - 1] = qmi(fact[N - 1], mod - 2) % mod;
for (int i = N - 2; i >= 1; i--) {
infact[i] = infact[i + 1] * (i + 1) % mod;
}
}
int C(int n, int m) {
if (m > n) return 0;
return fact[n] * infact[m] % mod * infact[n - m] % mod;
}
signed main() {
init();
cin >> n >> m;
int res = 0;
for (int i = 1; i <= m; i++) {
int now = i;
int temp = 1;
for (int j = 2; j <= now / j; j++) {
if (now % j == 0) {
int cnt = 0;
while (now % j == 0) {
now /= j;
cnt++;
}
temp = temp * C(n + cnt - 1, n - 1) % mod;
temp %= mod;
}
}
if (now > 1) temp = temp * C(n, n - 1) % mod;
temp %= mod;
res = (res + temp) % mod;
}
cout << res << endl;
}
D - I Wanna Win The Game
题意:
给定\(N\)和\(M\),现在问你有多少个序列满足下面的条件。\(A_i>=0\),\(\sum \limits_{i=1} ^{n}A_i=M\),\(A_1 xor A_2 xor A_3.....A_n=0\)
思路:
有异或操作,考虑将\(M\)分解成二进制\(01\)数来考虑,由于所有数的异或和为\(0\),则说明对于\(M\)的每一位,都必须要有偶数个\(1\),保证异或为\(0\),那么对于每一位就可以考虑有多少个\(1\),每一位有\(2,2×2,2×3....2×k\)个1,也就是说每一位的大小都可以是\(2×2^i,4×2^i....2×k×2^i\),这就相当于是分组背包,每个组可以挑选体积为\(2×k×2^i\)的物品,此时定义\(f(i,j)\)定义为考虑\(M\)分解成二进制后的前\(i\)位,体积为\(j\)的方案数,那么最终答案就是\(f(log_{2}M,M)\),转移方程为\(f(i,j)=f(i,j)+f(i-1,j-2×k×2^i)×{n\choose{2k}}\),相当于选出这些\(1\)后,分给\(n\)个空,方程可以优化前一维,最终输出\(f(M)\)就是答案
View Code
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5010;
const int mod = 998244353;
int fact[N], infact[N];
int n, m;
int f[N];
int qmi(int a, int k) {
int res = 1;
while (k) {
if (k & 1) res = res * a % mod;
k >>= 1;
a = a * a % mod;
}
return res;
}
void init() {
fact[0] = infact[0] = 1;
for (int i = 1; i < N; i++) {
fact[i] = fact[i - 1] * i % mod;
}
infact[N - 1] = qmi(fact[N - 1], mod - 2) % mod;
for (int i = N - 2; i >= 1; i--) {
infact[i] = infact[i + 1] * (i + 1) % mod;
}
}
int C(int n, int m) {
if (m > n) return 0;
return fact[n] * infact[m] % mod * infact[n - m] % mod;
}
signed main() {
init();
cin >> n >> m;
f[0] = 1;
for (int i = 0; (1 << i) <= m; i++) {
for (int j = m; j >= 0; j--) {
for (int k = 1; 2 * k * (1 << i) <= j; k++) {
if (j >= 2 * k * (1 << i)) {
f[j] += f[j - 2 * k * (1 << i)] * C(n, 2 * k) % mod;
f[j] %= mod;
}
}
}
}
cout << f[m] << endl;
}
E - Spread of Information
题意:
给定\(N\)个点的的树,现在可以选\(K\)个点感染病毒,病毒以每秒一个点的速度向周边感染,问通过放置病毒,感染整棵树的最短时间为多少?
思路:
可以先把问题转换成给定时间\(X\),来感染全图,最少需要多少病毒?可以发现时间是具有单调性的,所以是可以二分的,所以解决上面的问题,时间就可以用二分来枚举出来。给定时间来算感染全图需要的最少病毒数量,可以通过类树形\(dp\)搜索来解决
定义\(f(u)\)为在以\(u\)为子树的结点中,离它最近病毒的距离
\(g(u)\)在以\(u\)为子树的结点中,离它最远的未被感染的结点的距离
若\(f(u)+g(u)<=x\),说明以\(u\)为子树的所有结点都被感染了,所以当前点也会被感染,所以\(g(u)=-inf\)
若\(g(u)=x\),距离当前点最远的未被感染的点的距离已经到了\(x\),此时不放病毒的话,就会导致那个最远的点在限定时间内被感染,所以当前点必须被感染,\(f(u)=0,g(u)=-inf,res++\)
最后回溯到根节点\(1\),若\(g(1)≥0\)的话,说明病毒还没在时间内感染根结点,必须在根节点放置一个病毒
View Code
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
const int inf = 0x3f3f3f3f;
#define int long long
vector<int> e[N];
int n, k;
int f[N], g[N];
int res;
void dfs(int u, int fa, int limit) {
f[u] = inf, g[u] = 0;
for (auto v : e[u]) {
if (v == fa) continue;
dfs(v, u, limit);
f[u] = min(f[u], f[v] + 1);
g[u] = max(g[u], g[v] + 1);
}
if (f[u] + g[u] <= limit) {
g[u] = -inf;
} else if (g[u] == limit) {
f[u] = 0, g[u] = -inf;
res++;
}
}
bool check(int x) {
res = 0;
dfs(1, -1, x);
if (g[1] >= 0) res++;
return res <= k;
}
signed main() {
cin >> n >> k;
for (int i = 1, u, v; i < n; i++) {
cin >> u >> v;
e[u].push_back(v);
e[v].push_back(u);
}
int l = 0, r = n;
while (l < r) {
int mid = (l + r) >> 1;
if (check(mid)) {
r = mid;
} else {
l = mid + 1;
}
}
cout << l << endl;
}

浙公网安备 33010602011771号