読者です 読者をやめる 読者になる 読者になる

ゆらのふなびと

競プロ, Python, C++

yukicoder No.391 CODING WAR

yukicoder 場合の数 包除原理

問題

 N 人の人と  M 個の問題がある。次の制約をすべて満たす、人→問題の割り当て方の数を\mod 10^9 + 7 で求めよ。

  • 1人はただ1つの問題に取り組む

  • すべての問題について、取り組む人が1人以上いる

制約

 1 \leq N \leq 10^{12}

 1 \leq M \leq 10^5

解法

前提として、人は区別されるということに注意(これをわかっていなくて時間を溶かした)。

サンプル2  ( N = 7, M = 3 ) を考える。まずざっくり考えると、1人1人について、どの問題に取り組むかの3通りだから、 3^7 通りある。しかしこれでは1人も取り組まない問題がある場合も含んでいるので、包除原理で辻褄を合わせていく。まず誰にも取り組まれない問題を1つだけ固定すると、その問題の選び方が  _3 C_1 通り、人→問題の割り当て方は  2^7 通りなので  _3 C_1 * 2^7 通り。以下同様に考えていくとサンプル2の答えは  3^7 - _3 C_1 * 2^7 + _3 C_2 * 1^7 と求まる。一般の場合も同様の式で解くことができる。

#include <bits/stdc++.h>
using namespace std;
#define int long long   // <-----!!!!!!!!!!!!!!!!!!!

#define rep(i,n) for (int i=0;i<(n);i++)
#define rep2(i,a,b) for (int i=(a);i<(b);i++)
#define rrep(i,n) for (int i=(n)-1;i>=0;i--)
#define rrep2(i,a,b) for (int i=(b)-1;i>=(a);i--)
#define all(a) (a).begin(),(a).end()

typedef long long ll;
typedef pair<int, int> Pii;
typedef tuple<int, int, int> TUPLE;
typedef vector<int> V;
typedef vector<V> VV;
typedef vector<VV> VVV;
typedef vector<vector<int>> Graph;
const int inf = 1e9;
const int mod = 1e9 + 7;

ll powmod (ll a, ll p) {
    ll ans = 1;
    ll mul = a;
    for (; p > 0; p >>= 1, mul = (mul * mul) % mod) {
        if ((p & 1) == 1) ans = (ans * mul) % mod;
    }
    return ans;
}

const int MAX_N = 100010;

ll fact[MAX_N];
ll revFact[MAX_N];
// !!!!!!!!SET FACT!!!!!!!
void setFact(int N) {
    fact[0] = 1;
    for (int i = 1; i < N; i++) {
        fact[i] = fact[i - 1] * i;
        fact[i] %= mod;
    }
    revFact[N - 1] = powmod(fact[N - 1], mod - 2);
    for (int i = N - 2; i >= 0; i--) {
        revFact[i] = revFact[i + 1] * (i + 1);
        revFact[i] %= mod;
    }
}

ll getC(int a, int b) {
    return (((fact[a] * revFact[b]) % mod) * revFact[a - b]) % mod;
}

signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);

    int N, M;
    cin >> N >> M;
    setFact(MAX_N);
    int ans = 0;
    for (int x = M; x > 0; x--) {
        ans += ((M - x) % 2 ? -1 : 1) * getC(M, x) * powmod(x, N);
        ans %= mod;
    }

    cout << (ans + mod) % mod << endl;
}

包除原理の良い練習になった。