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;
}
posted @ 2022-03-18 19:40  Wraith_G  阅读(65)  评论(0)    收藏  举报
// //