0%

AtCoder Beginner Contest 156题解

#题目链接

D - Bouquet

#题意简述

有$n(2≤n≤10^9)$种花,每种花一枝,求可组成除了含有$a、b$枝花外共多少种花束。

#简要做法

显然能组成的花束种类最多有$2^n-1$种。那么答案即为$2^n-1-\binom{n}{a}-\binom{n}{b}$。用卢卡斯定理可以快速求解。

#参考代码

#pragma GCC opimize (3, "Ofast", "inline")
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod = 1e9 + 7;
int n, a, b;
inline ll fpow(int x, int k) {
    ll ret = 1, base = x;
    while(k) {
        if(k & 1) ret = ret * base % mod;
        base = base * base % mod;
        k >>= 1;
    }
    return ret;
}
inline ll C(ll n,ll m){
    if(n < m) return 0;
    if(m > n - m) m = n - m;
    ll a = 1, b = 1;
    for(int i = 0; i < m; ++i) {
        a = (a * (n - i)) % mod;
        b = (b * (i + 1)) % mod;
    }
    return a * fpow(b, mod - 2) % mod;
}
inline ll Lucas(ll n, ll m){
    if(!m) return 1;
    return Lucas(n/mod, m/mod) * C(n%mod,m%mod)%mod;
}
const int maxn = 2e5 + 5;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    cin >> n >> a >> b;
    ll ans = fpow(2, n) - 1;
    ans = (ans - Lucas(n, a) - Lucas(n, b)) % mod;
    cout << (ans + mod) % mod << endl;
    return 0;
}

#题目链接

E - Roaming

#题意简述

一共有$n(3≤n≤2×10^5)$个房间,每个房间中有1个人。每次移动中,一个人可以从所在的房间到达任意一个房间。共有$k(2≤k≤2×10^9)$次移动机会。求最终共有多少种可能的房间情况。

#简要做法

可以枚举共有多少个房间里没有人,这样的房间最少$0$个,最多$min(n-1,k)$个。然后对于有人的房间,用插板法求出可能数量。

#参考代码

#pragma GCC opimize (3, "Ofast", "inline")
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod = 1e9 + 7;
const int maxn = 2e5 + 5;
int n, k;
ll f[maxn], finv[maxn];
inline ll ksm(int x, int k) {
    ll ret = 1, base = x;
    while(k) {
        if(k & 1) ret = ret * base % mod;
        base = base * base % mod, k >>= 1;
    }
    return ret;
}
inline ll C(int n, int m) {
    return f[n] * (finv[n-m] * finv[m] % mod) % mod;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    cin >> n >> k;
    f[0] = f[1] = 1;
    for(int i = 2; i <= n + 1; ++i) f[i] = f[i - 1] * i % mod;
    finv[n + 1] = ksm(f[n + 1], mod - 2); 
    for(int i = n; i >= 0; --i) finv[i] = finv[i + 1] * (i + 1) % mod;
    ll ans = 0;
    for(int i = 0; i <= min(n-1, k); ++i) {
        ans = (ans + C(n, i) * C(n-1, n-i-1)) % mod;
    }
    cout << ans << endl;
    return 0;
}

F题公式编辑器挂了,先扔一边。。。