ferinの競プロ帳

競プロについてのメモ

GCJ 2019 R1B Fair Fight

問題ページ

解法

部分点は  O(N^{3}) で取りうる区間を全探索すればよい。
満点では  O(N^{2}) 個の区間を全部見ているのでは間に合わない。 c \lbrack i \rbrack が最大になり、 \max (d \lbrack i \rbrack) についても条件を満たすような区間がそれぞれ何個あるか?を各  c \lbrack i \rbrack に対して求めることで答えを求める。この各区間に課される条件を3つに分割することで数えやすくする。
(1)  \max (c \lbrack l \rbrack, \ldots, c \lbrack r \rbrack) = c \lbrack i \rbrack となるような最大の区間 l,r
(2)  \max (c \lbrack l \rbrack, \ldots, c \lbrack r \rbrack) \leq c \lbrack i \rbrack + k となるような最大の区間  l,r
(3)  \max (c \lbrack l \rbrack, \ldots, c \lbrack r \rbrack) \lt c \lbrack i \rbrack - k となるような最大の区間  l,r

(1)~(3)のそれぞれについて最大の区間に内包される全ての区間についても条件を満たす。よって条件を満たす区間 (i-l+1) \times (r-i+1) 個となる。また、(1)について同じ区間を複数回数えないようにするため区間の最大値が複数ある場合は最も左の  c \lbrack i \rbrack を選ぶとする。

答えは ((1)と(2)を両方を満たすような区間の数)-((1)と(3)の両方を満たすような区間の数) となる。よってこれらの区間の数を高速に求めることができればよい。(1)を満たすような区間 l_1, r_1 で(2)を満たすような区間 l_2, r_2 であるとき、これら両方を満たすような区間 \max (l_1, l_2) から  \min (r_1, r_2)となる。したがって(1)~(3)を求めることができれば答えがわかる。

(1)の  l を求めることを考える。 r=i として  \max (c \lbrack j \rbrack, \ldots, c \lbrack r \rbrack) = c \lbrack i \rbrack を満たすような最大の  j が求まればよい。これは区間の最大値が単調増加することから二分探索を使って求めることができる。sparse table等の  O(1) でRMQができるデータ構造を用いることで  O(N \log N) で各  i について l を求めることができる。その他の  l,r についても同様に二分探索を行うことで求めることができ  O(N \log N) で解くことができた。

(1)で重複して数えないようにするため  c \lbrack i \rbrack i のpairの区間minを求めるとした。(2)(3)の区間が空の場合に注意。

#include <bits/stdc++.h>

using namespace std;
using ll = long long;
// #define int ll
using PII = pair<ll, ll>;

#define FOR(i, a, n) for (ll i = (ll)a; i < (ll)n; ++i)
#define REP(i, n) FOR(i, 0, n)
#define ALL(x) x.begin(), x.end()

template<typename T> T &chmin(T &a, const T &b) { return a = min(a, b); }
template<typename T> T &chmax(T &a, const T &b) { return a = max(a, b); }
template<typename T> bool IN(T a, T b, T x) { return a<=x&&x<b; }
template<typename T> T ceil(T a, T b) { return a/b + !!(a%b); }

template<typename T> vector<T> make_v(size_t a) { return vector<T>(a); }
template<typename T,typename... Ts>
auto make_v(size_t a,Ts... ts) {
    return vector<decltype(make_v<T>(ts...))>(a,make_v<T>(ts...));
}
template<typename T,typename V> typename enable_if<is_class<T>::value==0>::type
fill_v(T &t, const V &v) { t=v; }
template<typename T,typename V> typename enable_if<is_class<T>::value!=0>::type
fill_v(T &t, const V &v ) { for(auto &e:t) fill_v(e,v); }

template<class S,class T>
ostream &operator <<(ostream& out,const pair<S,T>& a){
    out<<'('<<a.first<<','<<a.second<<')'; return out;
}
template<class T>
ostream &operator <<(ostream& out,const vector<T>& a){
    out<<'['; for(T i: a) {out<<i<<',';} out<<']'; return out;
}

int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0}; // DRUL
const int INF = 1<<30;
const ll LLINF = 1LL<<60;
const ll MOD = 1000000007;

template <typename S>
struct sparseTable {
    using T = typename S::T;
    int n;
    vector<int> log2;
    vector<vector<T>> t;

    sparseTable(int nn) {
        n = nn;
        log2.assign(n+1, 0);
        for(int i=2; i<=n; ++i) log2[i] = log2[i >> 1] + 1;
        t = vector<vector<T>>(log2[n]+1, vector<T>(n));
    }
    void init(vector<T> v) {
        for(int i=0; i<n; ++i) t[0][i] = v[i];
        for(int j=1; j<=log2[n]; ++j) {
            int w = 1LL<<(j-1);
            for (int i = 0; i+(w<<1) <= n; ++i) {
                t[j][i] = S::op(t[j-1][i], t[j-1][i+w]);
            }
        }
    }
    // [l, r]
    T query(int l, int r) {
        int j = log2[r - l];
        return S::op(t[j][l], t[j][r-(1 << j)+1]);
    }
};

// 集合T、結合則・可換・冪等律が成り立つ二項演算op
struct maximum {
    using T = ll;
    static T op(const T& a, const T& b) { return max(a, b); }
};
struct maximum_P {
    using T = PII;
    static T op(const T& a, const T& b) { return max(a, b); }
};

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

    ll test;
    cin >> test;
    REP(tes, test) {
        ll n, k;
        cin >> n >> k;
        vector<ll> c(n), d(n);
        REP(i, n) cin >> c[i];
        REP(i, n) cin >> d[i];

        vector<PII> cc(n);
        REP(i, n) cc[i] = {c[i], i};
        sparseTable<maximum_P> seg1(n);
        seg1.init(cc);

        sparseTable<maximum> seg2(n);
        seg2.init(d);

        ll ret = 0;
        REP(i, n) {
            // max(c[l],…,c[r]) = c[i] となるようなl,r
            ll l1, r1;
            ll lb=-1, ub=i;
            while(ub-lb>1) {
                ll mid = (lb+ub)/2;
                if(seg1.query(mid, i) <= cc[i]) ub = mid;
                else lb = mid;
            }
            l1 = ub;
            lb=i, ub=n;
            while(ub-lb>1) {
                ll mid = (lb+ub)/2;
                if(seg1.query(i, mid) <= cc[i]) lb = mid;
                else ub = mid;
            }
            r1 = lb;

            // max(d[l],…,d[r]) <= c[i]+k となるようなl,r
            if(d[i] > c[i]+k) continue;
            ll l2, r2;
            lb=-1, ub=i;
            while(ub-lb>1) {
                ll mid = (lb+ub)/2;
                if(seg2.query(mid, i) <= c[i]+k) ub = mid;
                else lb = mid;
            }
            l2 = ub;
            lb=i, ub=n;
            while(ub-lb>1) {
                ll mid = (lb+ub)/2;
                if(seg2.query(i, mid) <= c[i]+k) lb = mid;
                else ub = mid;
            }
            r2 = lb;

            // max(d[l],…,d[r]) < c[i]-k となるようなl,r
            ll l3, r3;
            lb=-1, ub=i;
            while(ub-lb>1) {
                ll mid = (lb+ub)/2;
                if(seg2.query(mid, i) < c[i]-k) ub = mid;
                else lb = mid;
            }
            l3 = ub;
            lb=i, ub=n;
            while(ub-lb>1) {
                ll mid = (lb+ub)/2;
                if(seg2.query(i, mid) < c[i]-k) lb = mid;
                else ub = mid;
            }
            r3 = lb;

            // max(c[l],…,c[r])=c[i] かつ max(d[l],…,d[r])<=c[i]+k
            ll l = max(l1, l2), r = min(r1, r2);
            ret += (i-l+1) * (r-i+1);
            // max(c[l],…,c[r])=c[i] かつ max(d[l],…,d[r])<c[i]-k
            if(d[i] < c[i]-k) {
                l = max(l1, l3), r = min(r1, r3);
                ret -= (i-l+1) * (r-i+1);
            }
        }

        cout << "Case #" << tes+1 << ": ";
        cout << ret << endl;
    }

    return 0;
}

Tenka1 Programmer Contest 2019 E - Polynomial Divisors

問題ページ

解法

解説放送で言ってることを自分流に書いたもの
pを一つ固定してその素数が条件を満たすか判定することを考える。pで割り切れるか考えるので以降ではmod pで考える。任意のxに対してf(x)=0 mod pとなればよい。フェルマーの小定理よりx=x^pである。したがって
f(x)
= a[N]x^N + a[N-1]x^(N-1) + … + a[1]x + a[0]
= (a[p-1]+a[2p-2]+…)x^(p-1) + … + (a[2]+a[p+1]+…)x^2 + (a[1]+a[p]+…)x + a[0]
となる。多項式がどのようなxに対しても0になる⇔係数が全て0 であるから係数が全て0か判定することでpが条件を満たすか判定することができる。係数を足してpの倍数になっているかの判定をO(N)ですればよい。
あとはpの候補全てに対してこの判定を行えばよい。pの候補の数を絞ることで全ての候補に対して判定を行っても間に合うようにする。

  • p<=N
    10^4以下の素数は大した個数がないので全ての素数に対して判定を行う
  • p>N
    フェルマーの小定理を使って変形したようにx^iの係数が複数のaの和になることがない。よってaNがpの倍数でないと条件を満たすことはない。したがってpの候補となるのはa[n]の素因数のみであり大した個数ではないので全てに対して判定を行えばよい。a[n]<0の場合に注意
#include <bits/stdc++.h>

using namespace std;
using ll = long long;
// #define int ll
using PII = pair<ll, ll>;

#define FOR(i, a, n) for (ll i = (ll)a; i < (ll)n; ++i)
#define REP(i, n) FOR(i, 0, n)
#define ALL(x) x.begin(), x.end()

template<typename T> T &chmin(T &a, const T &b) { return a = min(a, b); }
template<typename T> T &chmax(T &a, const T &b) { return a = max(a, b); }
template<typename T> bool IN(T a, T b, T x) { return a<=x&&x<b; }
template<typename T> T ceil(T a, T b) { return a/b + !!(a%b); }

template<typename T> vector<T> make_v(size_t a) { return vector<T>(a); }
template<typename T,typename... Ts>
auto make_v(size_t a,Ts... ts) {
    return vector<decltype(make_v<T>(ts...))>(a,make_v<T>(ts...));
}
template<typename T,typename V> typename enable_if<is_class<T>::value==0>::type
fill_v(T &t, const V &v) { t=v; }
template<typename T,typename V> typename enable_if<is_class<T>::value!=0>::type
fill_v(T &t, const V &v ) { for(auto &e:t) fill_v(e,v); }

template<class S,class T>
ostream &operator <<(ostream& out,const pair<S,T>& a){
    out<<'('<<a.first<<','<<a.second<<')'; return out;
}
template<class T>
ostream &operator <<(ostream& out,const vector<T>& a){
    out<<'['; for(T i: a) {out<<i<<',';} out<<']'; return out;
}

int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0}; // DRUL
const int INF = 1<<30;
const ll LLINF = 1LL<<60;
const ll MOD = 1000000007;

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

    ll n;
    cin >> n;
    vector<ll> a(n+1);
    REP(i, n+1) cin >> a[n-i];

    auto test = [&](ll p) {
        if(a[0]%p) return false;
        vector<ll> sum(p);
        FOR(i, 1, n+1) sum[i%(p-1)] += a[i];
        bool flag = true;
        REP(i, p) if(sum[i]%p != 0) flag = false;
        return flag;
    };

    vector<ll> ans;

    // P <= N
    vector<bool> prime(n+2, true);
    prime[0] = prime[1] = false;
    for (int i = 2; i <= n; i++) {
        if (prime[i]) {
            if(test(i)) ans.push_back(i);
            for (int j = 2 * i; j <= n; j += i) {
                prime[j] = false;
            }
        }
    }

    // P > N
    ll t = abs(a[n]);
    vector<ll> div;
    for(ll i=2; i*i<=t; ++i) {
        if(t%i==0) div.push_back(i);
        while(t%i==0) t/=i;
    }
    if(t>1) div.push_back(t);
    for(auto i: div) {
        if(i <= n) continue;
        bool flag = true;
        REP(j, n+1) if(a[j]%i != 0) flag = false;
        if(flag) ans.push_back(i);
    }

    for(auto i: ans) cout << i << endl;

    return 0;
}

Tenka1 Programmer Contest 2019 D - Three Colors

問題ページ

考えたこと

  • 部分和問題っぽくて貪欲は無理そうだし制約小さいしまあDP
  • dp[i番目まで][R=j][G=j] みたいなDPを考える
  • R,G,Bは90000くらいまで取りうるのでどう考えても無理
  • 三角形の成立条件を考える
  • R+G>B && R+B>G && B+G>R
  • どれか一つ和を持っておけば済むとかがあると嬉しいけど思いつかない
  • 条件の否定を取って補集合の方を数えることにしてみる
  • R+G<=B || R+B<=G || B+G<=R になる
  • 和集合を数えたいので包除原理っぽい
  • R+G<=Bだけを満たすのを数えるだけならDPすればできる
  • 対称性があるので3倍すればよさそう
  • 2つ条件を満たす場合を数え上げたい
  • R+G<=B && R+B<=G
  • いろいろ考えてしばらく迷走する
  • これ満たすのB=G, R=0 しかない
  • R=0 or G=0 or B=0 だったら三角形ができることはない
  • R>0 && G>0 && B>0 && R+G<=B のパターンを数え上げる
  • 対称性でこれを3倍する
  • 全体(R!=0&&G!=0&&B!=0の塗り方)からこの値を引けば答えになる
  • dp[i番目まで][j=R+G][k=(R>0)][l=(G>0)] = (組み合わせ数) としてDP

modintバグってたのとmake_vをグローバルにしたら爆速になったのとかが虚無だった

#include <bits/stdc++.h>

using namespace std;
using ll = long long;
// #define int ll
using PII = pair<ll, ll>;

#define FOR(i, a, n) for (ll i = (ll)a; i < (ll)n; ++i)
#define REP(i, n) FOR(i, 0, n)
#define ALL(x) x.begin(), x.end()

template<typename T> T &chmin(T &a, const T &b) { return a = min(a, b); }
template<typename T> T &chmax(T &a, const T &b) { return a = max(a, b); }
template<typename T> bool IN(T a, T b, T x) { return a<=x&&x<b; }
template<typename T> T ceil(T a, T b) { return a/b + !!(a%b); }

template<typename T> vector<T> make_v(size_t a) { return vector<T>(a); }
template<typename T,typename... Ts>
auto make_v(size_t a,Ts... ts) {
    return vector<decltype(make_v<T>(ts...))>(a,make_v<T>(ts...));
}
template<typename T,typename V> typename enable_if<is_class<T>::value==0>::type
fill_v(T &t, const V &v) { t=v; }
template<typename T,typename V> typename enable_if<is_class<T>::value!=0>::type
fill_v(T &t, const V &v ) { for(auto &e:t) fill_v(e,v); }

template<class S,class T>
ostream &operator <<(ostream& out,const pair<S,T>& a){
    out<<'('<<a.first<<','<<a.second<<')'; return out;
}
template<class T>
ostream &operator <<(ostream& out,const vector<T>& a){
    out<<'['; for(T i: a) {out<<i<<',';} out<<']'; return out;
}

int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0}; // DRUL
const int INF = 1<<30;
const ll LLINF = 1LL<<60;
// const ll MOD = 1000000007;

template<ll MOD>
struct modint {
    ll x;
    modint(): x(0) {}
    modint(ll y) : x(y>=0 ? y%MOD : y%MOD+MOD) {}
    static constexpr ll mod() { return MOD; }
    // e乗
    modint pow(ll e) {
        ll a = 1, p = x;
        while(e > 0) {
            if(e%2 == 0) {p = (p*p) % MOD; e /= 2;}
            else {a = (a*p) % MOD; e--;}
        }
        return modint(a);
    }
    modint inv() const {
        ll a=x, b=MOD, u=1, y=1, v=0, z=0;
        while(a) {
            ll q = b/a;
            swap(z -= q*u, u);
            swap(y -= q*v, v);
            swap(b -= q*a, a);
        }
        return z;
    }
    // Comparators
    bool operator <(modint b) { return x < b.x; }
    bool operator >(modint b) { return x > b.x; }
    bool operator<=(modint b) { return x <= b.x; }
    bool operator>=(modint b) { return x >= b.x; }
    bool operator!=(modint b) { return x != b.x; }
    bool operator==(modint b) { return x == b.x; }
    // Basic Operations
    modint operator+(modint r) const { return modint(*this) += r; }
    modint operator-(modint r) const { return modint(*this) -= r; }
    modint operator*(modint r) const { return modint(*this) *= r; }
    modint operator/(modint r) const { return modint(*this) /= r; }
    modint &operator+=(modint r) {
        if((x += r.x) >= MOD) x -= MOD;
        return *this;
    }
    modint &operator-=(modint r) {
        if((x -= r.x) < 0) x += MOD;
        return *this;
    }
    modint &operator*=(modint r) { x = x * r.x % MOD; return *this; }
    modint &operator/=(modint r) { return *this *= r.inv(); }
    // increment, decrement
    modint operator++() { x++; return *this; }
    modint operator++(signed) { modint t = *this; x++; return t; }
    modint operator--() { x--; return *this; }
    modint operator--(signed) { modint t = *this; x--; return t; }
};
using mint = modint<998244353>;
template<class T> mint operator*(T l, mint r) { return mint(l) *= r; }
template<class T> mint operator+(T l, mint r) { return mint(l) += r; }
template<class T> mint operator-(T l, mint r) { return mint(l) -= r; }
template<class T> mint operator/(T l, mint r) { return mint(l) /= r; }
template<class T> bool operator==(T l, mint r) { return mint(l) == r; }
template<class T> bool operator!=(T l, mint r) { return mint(l) != r; }
// Input/Output
ostream &operator<<(ostream& os, mint a) { return os << a.x; }
istream &operator>>(istream& is, mint &a) { return is >> a.x; }
string to_frac(mint v) {
    static map<ll, PII> mp;
    if(mp.empty()) {
        mp[0] = mp[mint::mod()] = {0, 1};
        FOR(i, 2, 1001) FOR(j, 1, i) if(__gcd(i, j) == 1) {
            mp[(mint(i) / j).x] = {i, j};
        }
    }
    auto itr = mp.lower_bound(v.x);
    if(itr != mp.begin() && v.x - prev(itr)->first < itr->first - v.x) --itr;
    string ret = to_string(itr->second.first + itr->second.second * ((int)v.x - itr->first));
    if(itr->second.second > 1) {
        ret += '/';
        ret += to_string(itr->second.second);
    }
    return ret;
}

mint dp[300][90001][2][2];
signed main(void)
{
    cin.tie(0);
    ios::sync_with_stdio(false);

    ll n;
    cin >> n;
    ll sum = 0;
    vector<ll> a(n);
    REP(i, n) cin >> a[i], sum += a[i];

    dp[0][0][0][0] = 1;
    dp[0][a[0]][1][0] = 1;
    dp[0][a[0]][0][1] = 1;
    FOR(i, 1, n) REP(j, sum+1) REP(k, 2) REP(l, 2) {
        if(j>=a[i]) {
            dp[i][j][k|1][l] += dp[i-1][j-a[i]][k][l];
            dp[i][j][k][l|1] += dp[i-1][j-a[i]][k][l];
        }
        dp[i][j][k][l] += dp[i-1][j][k][l];
    }

    mint ret = 0;
    FOR(i, 1, sum) if(i <= sum-i) ret += dp[n-1][i][1][1];
    cout << mint(3).pow(n) - mint(2).pow(n)*3 + 3 - ret*3 << endl;

    return 0;
}

乱択

https://codeforces.com/blog/entry/61587 の必要なところのメモ

乱数生成

rand()

乱数生成の質が微妙なのでやめよう
こどふぉだと最大値が32767しかないのでさらにまずい

mt19937

C++11以上で追加された機能でメルセンヌツイスターで乱数を生成する

  • 高速
  • 最大値が2³¹-1
  • 乱数の質もいい

64bitの乱数がほしいならmt19937_64を使うとできる

乱数のseedが他人にバレるとまずい
chrono::steady_clock::now().time_since_epoch().count()
とか
(uint64_t) new char
とかを使おう

一様乱数の生成

rand() % 1000 → 一様にならないのでだめ
uniform_int_distribution → これを使おう

配列のシャッフル

random_shuffle → 内部でrandを使ってるのでやめよう
shuffle → こっちを使おう

一様乱数を生成するコード

struct dice {
    mt19937 mt;
    dice() : mt(chrono::steady_clock::now().time_since_epoch().count()) {}
    // [0, x)の一様乱数
    ll operator()(ll x) { return this->operator()(0, x - 1); }
    // [x, y]の一様乱数
    ll operator()(ll x, ll y) {
        uniform_int_distribution<ll> dist(x, y);
        return dist(mt);
    }
} rnd;

int main() {
    cout << rnd(100) << endl;
    cout << rnd(30, 80) << endl;
}

rand()をどうやって落とす?

https://codeforces.com/blog/entry/61675

Codeforces Round #539 (Div. 2) F. Sasha and Interesting Fact from Graph Theory

問題ページ

解法

頂点a,bを結ぶパスの辺をe本と固定したときの組み合わせ数を求める。まずn-2個の頂点からパス上のe-1個の頂点を選ぶ方法がP(n-2, e-1)通りである。次にパス上の辺の重みを決定する方法について考える。mをe分割する方法はm個のボールの間(m-1個)にe-1個の仕切りを入れると考えることができC(m-1, e-1)通りである。
パス以外の辺の重みは自由に決められるのでm^(n-1-e)通りである。ラベルつきの森が何通りあるか求めるにはCayley's_formulaを用いればよい。頂点数n、連結成分数kの森はk*n^(n-1-k)通りである。今回の条件設定に当てはめると(e+1)*n^(n-2-e)通りである。e=n-1のときに注意。
まとめると辺数がeのときP(n-2,e-1)*C(m-1,e-1)*m^(n-1-e)*(e+1)*n^(n-2-e)通りとなる。これを1<=e<=n-1について足せばよい。

#include <bits/stdc++.h>

using namespace std;
using ll = long long;
// #define int ll
using PII = pair<ll, ll>;

#define FOR(i, a, n) for (ll i = (ll)a; i < (ll)n; ++i)
#define REP(i, n) FOR(i, 0, n)
#define ALL(x) x.begin(), x.end()

template<typename T> T &chmin(T &a, const T &b) { return a = min(a, b); }
template<typename T> T &chmax(T &a, const T &b) { return a = max(a, b); }
template<typename T> bool IN(T a, T b, T x) { return a<=x&&x<b; }
template<typename T> T ceil(T a, T b) { return a/b + !!(a%b); }

template<typename T> vector<T> make_v(size_t a) { return vector<T>(a); }
template<typename T,typename... Ts>
auto make_v(size_t a,Ts... ts) { 
    return vector<decltype(make_v<T>(ts...))>(a,make_v<T>(ts...));
}
template<typename T,typename V> typename enable_if<is_class<T>::value==0>::type
fill_v(T &t, const V &v) { t=v; }
template<typename T,typename V> typename enable_if<is_class<T>::value!=0>::type
fill_v(T &t, const V &v ) { for(auto &e:t) fill_v(e,v); }

template<class S,class T>
ostream &operator <<(ostream& out,const pair<S,T>& a){
    out<<'('<<a.first<<','<<a.second<<')'; return out;
}
template<class T>
ostream &operator <<(ostream& out,const vector<T>& a){
    out<<'['; for(T i: a) {out<<i<<',';} out<<']'; return out;
}

int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0}; // DRUL
const int INF = 1<<30;
const ll LLINF = 1LL<<60;
const ll MOD = 1000000007;

template<ll MOD>
struct modint {
    ll x;
    modint(): x(0) {}
    modint(ll y) : x(y>=0 ? y%MOD : y%MOD+MOD) {}
    ll get() const { return x; }
    // e乗
    modint pow(ll e) {
        ll a = 1, p = x;
        while(e > 0) {
            if(e%2 == 0) {p = (p*p) % MOD; e /= 2;}
            else {a = (a*p) % MOD; e--;}
        }
        return modint(a);
    }
    ll extgcd(ll a, ll b, ll &x, ll &y) {
        ll g = a; x = 1, y = 0;
        if(b != 0) g = extgcd(b, a%b, y, x), y -= (a/b) * x;
        return g;
    }
    modint inv() {
        ll s, t; extgcd(x, MOD, s, t);
        return modint(s);
    }
    // Comparators
    bool operator <(modint b) { return x < b.x; }
    bool operator >(modint b) { return x > b.x; }
    bool operator<=(modint b) { return x <= b.x; }
    bool operator>=(modint b) { return x >= b.x; }
    bool operator!=(modint b) { return x != b.x; }
    bool operator==(modint b) { return x == b.x; }
    // increment, decrement
    modint operator++() { x++; return *this; }
    modint operator++(signed) { modint t = *this; x++; return t; }
    modint operator--() { x--; return *this; }
    modint operator--(signed) { modint t = *this; x--; return t; }
    // Basic Operations
    modint &operator+=(modint that) {
        x += that.x;
        if(x >= MOD) x -= MOD;
        return *this;
    }
    modint &operator-=(modint that) {
        x -= that.x;
        if(x < 0) x += MOD;
        return *this;
    }
    modint &operator*=(modint that) {
        x = (ll)x * that.x % MOD;
        return *this;
    }
    modint &operator/=(modint that) {
        x = (ll)x * that.inv().x % MOD;
        return *this;
    }
    modint &operator%=(modint that) {
        x = (ll)x % that.x;
        return *this;
    }
    modint operator+(modint that) const { return *this += that; }
    modint operator-(modint that) const { return *this -= that; }
    modint operator*(modint that) const { return *this *= that; }
    modint operator/(modint that) const { return *this /= that; }
    modint operator%(modint that) const { return *this %= that; }
};
using mint = modint<1000000007>;
// Input/Output
ostream &operator<<(ostream& os, mint a) { return os << a.x; }
istream &operator>>(istream& is, mint &a) { return is >> a.x; }

ll binpow(ll x, ll e) {
  ll ret = 1, p = x;
  while(e > 0) {
    if(e&1) {(ret *= p) %= MOD; e--;}
    else {(p *= p) %= MOD; e /= 2;} 
  }
  return ret;
}

template<bool bigN=false>
ll combi(ll N_, ll K_, ll mo=MOD) {
    const int NUM_=1e6+10;
    static ll fact[NUM_+1]={},factr[NUM_+1]={},inv[NUM_+1]={};
    auto binpow = [&](ll x, ll e) -> ll {
        ll a = 1, p = x;
        while(e > 0) {
        if(e%2 == 0) {p = (p*p) % mo; e /= 2;}
        else {a = (a*p) % mo; e--;}
        }
        return a;
    };
    if (fact[0]==0) {
        fact[0] = factr[0] = inv[0] = 1;
        FOR(i, 1, NUM_+1) fact[i] = fact[i-1] * i % MOD;
        factr[NUM_] = binpow(fact[NUM_], mo-2);
        for(int i=NUM_-1; i>=0; --i) factr[i] = factr[i+1] * (i+1) % MOD;
        // bigNがないならいらない
        // REP(i, NUM_+1) inv[i] = binpow(i, MOD-2);
    }
    if(K_<0 || K_>N_) return 0;
    // 前計算 O(max(N,K)) クエリ O(1)
    if(!bigN) return factr[K_]*fact[N_]%MOD*factr[N_-K_]%MOD;
    // Nが大きいけどKが小さい場合に使う 前計算 O(Klog(mod)) クエリ O(K)
    ll ret = 1;
    for(;K_>0;N_--,K_--) (ret *= N_%MOD) %= MOD, (ret *= inv[K_]) %= MOD;
    return ret;
}

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

    ll n, m, a, b;
    cin >> n >> m >> a >> b;

    mint ret = 0, fact = 1;
    FOR(e, 1, n) {
        mint tmp = fact;
        tmp *= combi(m-1, e-1);
        tmp *= binpow(m, n-1-e);
        if(e != n-1) {
            tmp *= e+1;
            tmp *= binpow(n, n-2-e);
        }
        fact *= n-e-1;
        ret += tmp;
    }
    cout << ret << endl;

    return 0;
}

エクサウィザーズ 2019 E - Black or White

問題ページ

考えたこと

  • i回目までに両方ある/白しかない/黒しかない確率をそれぞれ求めたい
  • これがわかればi回目の答えは (両方ある確率)/2 + (黒しかない確率) になるので解ける
  • 黒しかないを知るには黒がj個あるがわかるとよさそう
  • dp[i番目][黒がj個] = 確率 をやろうとする
  • どうがんばってもインラインDPにはならない
  • 確率を直接求めてみる
  • 両方ある確率を求めるより白しかない/黒しかない確率を求めた方が楽そう
  • 白しかない→i回目までに黒をB個取った
  • i<Bならありえないので0
  • それ以外なら C(i,B) (1/2)^i とかになりそう
  • 黒しかないのも同様
  • 書いてみるとサンプルが合わない
  • i回目以前ですでに白/黒だけになってる分がおかしい
  • 白/黒だけになったらそれ以降はずっと同じ操作をするので変わらない
  • sum_{j=0}^i C(j-1,B-1) (1/2)^i でよさそう
  • O(B+W)で求められるので解けた
#include <bits/stdc++.h>

using namespace std;
using ll = long long;
// #define int ll
using PII = pair<ll, ll>;

#define FOR(i, a, n) for (ll i = (ll)a; i < (ll)n; ++i)
#define REP(i, n) FOR(i, 0, n)
#define ALL(x) x.begin(), x.end()

template<typename T> T &chmin(T &a, const T &b) { return a = min(a, b); }
template<typename T> T &chmax(T &a, const T &b) { return a = max(a, b); }
template<typename T> bool IN(T a, T b, T x) { return a<=x&&x<b; }
template<typename T> T ceil(T a, T b) { return a/b + !!(a%b); }

template<typename T> vector<T> make_v(size_t a) { return vector<T>(a); }
template<typename T,typename... Ts>
auto make_v(size_t a,Ts... ts) {
    return vector<decltype(make_v<T>(ts...))>(a,make_v<T>(ts...));
}
template<typename T,typename V> typename enable_if<is_class<T>::value==0>::type
fill_v(T &t, const V &v) { t=v; }
template<typename T,typename V> typename enable_if<is_class<T>::value!=0>::type
fill_v(T &t, const V &v ) { for(auto &e:t) fill_v(e,v); }

template<class S,class T>
ostream &operator <<(ostream& out,const pair<S,T>& a){
    out<<'('<<a.first<<','<<a.second<<')'; return out;
}
template<class T>
ostream &operator <<(ostream& out,const vector<T>& a){
    out<<'['; for(T i: a) {out<<i<<',';} out<<']'; return out;
}

int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0}; // DRUL
const int INF = 1<<30;
const ll LLINF = 1LL<<60;
const ll MOD = 1000000007;

struct mint {
    ll x;
    mint(): x(0) { }
    mint(ll y) : x(y>=0 ? y%MOD : y%MOD+MOD) {}
    ll get() const { return x; }
    // e乗
    mint pow(ll e) {
        ll a = 1, p = x;
        while(e > 0) {
            if(e%2 == 0) {p = (p*p) % MOD; e /= 2;}
            else {a = (a*p) % MOD; e--;}
        }
        return mint(a);
    }
    // Comparators
    bool operator <(mint b) { return x < b.x; }
    bool operator >(mint b) { return x > b.x; }
    bool operator<=(mint b) { return x <= b.x; }
    bool operator>=(mint b) { return x >= b.x; }
    bool operator!=(mint b) { return x != b.x; }
    bool operator==(mint b) { return x == b.x; }
    // increment, decrement
    mint operator++() { x++; return *this; }
    mint operator++(signed) { mint t = *this; x++; return t; }
    mint operator--() { x--; return *this; }
    mint operator--(signed) { mint t = *this; x--; return t; }
    // Basic Operations
    mint &operator+=(mint that) {
        x += that.x;
        if(x >= MOD) x -= MOD;
        return *this;
    }
    mint &operator-=(mint that) {
        x -= that.x;
        if(x < 0) x += MOD;
        return *this;
    }
    mint &operator*=(mint that) {
        x = (ll)x * that.x % MOD;
        return *this;
    }
    mint &operator/=(mint that) {
        x = (ll)x * that.pow(MOD-2).x % MOD;
        return *this;
    }
    mint &operator%=(mint that) {
        x = (ll)x % that.x;
        return *this;
    }
    mint operator+(mint that) const { return mint(*this) += that; }
    mint operator-(mint that) const { return mint(*this) -= that; }
    mint operator*(mint that) const { return mint(*this) *= that; }
    mint operator/(mint that) const { return mint(*this) /= that; }
    mint operator%(mint that) const { return mint(*this) %= that; }
};
// Input/Output
ostream &operator<<(ostream& os, mint a) { return os << a.x; }
istream &operator>>(istream& is, mint &a) { return is >> a.x; }

template<bool bigN=false>
ll combi(ll N_, ll K_, ll mo=MOD) {
  const int NUM_=5e5;
  static ll fact[NUM_+1]={},factr[NUM_+1]={},inv[NUM_+1]={};
  auto binpow = [&](ll x, ll e) -> ll {
    ll a = 1, p = x;
    while(e > 0) {
      if(e%2 == 0) {p = (p*p) % mo; e /= 2;}
      else {a = (a*p) % mo; e--;}
    }
    return a;
  };
  if (fact[0]==0) {
    fact[0] = factr[0] = inv[0] = 1;
    FOR(i, 1, NUM_+1) fact[i] = fact[i-1] * i % MOD;
    factr[NUM_] = binpow(fact[NUM_], mo-2);
    for(int i=NUM_-1; i>=0; --i) factr[i] = factr[i+1] * (i+1) % MOD;
    // bigNがないならいらない
    // REP(i, NUM_+1) inv[i] = binpow(i, MOD-2);
  }
  if(K_<0 || K_>N_) return 0;
  // 前計算 O(max(N,K)) クエリ O(1)
  if(!bigN) return factr[K_]*fact[N_]%MOD*factr[N_-K_]%MOD;
  // Nが大きいけどKが小さい場合に使う 前計算 O(Klog(mod)) クエリ O(K)
  ll ret = 1;
  for(;K_>0;N_--,K_--) (ret *= N_%MOD) %= MOD, (ret *= inv[K_]) %= MOD;
  return ret;
}

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

    ll b, w;
    cin >> b >> w;

    mint prob1_a = 0, prob2_a = 0;
    REP(i, b+w) {
        // 白しかない
        mint prob1;
        if(i < b) prob1 = 0;
        else prob1 = (mint(1) / 2).pow(i) * combi(i-1, b-1);
        prob1_a += prob1;
        // 黒しかない
        mint prob2;
        if(i < w) prob2 = 0;
        else prob2 = (mint(1) / 2).pow(i) * combi(i-1, w-1);
        prob2_a += prob2;
        // 両方ある
        mint prob3 = mint(1) - prob1_a - prob2_a;
        mint ret = prob2_a + prob3 / 2;
        cout << ret << endl;
    }

    return 0;
}

エクサウィザーズ 2019 D - Modulo Operations

問題ページ

考えたこと

  • sの順列でX%s_1%s_2%s_3…を求める
  • 制約を見るとDPをしてくださいと言っている
  • dp[i番目][modがj]みたいなの
  • 挿入DP?みたいな感じになりそうだけど遷移がやばそう
  • ここでmodの性質を思い出すとmod xをしたあとにmod y(y >= x)をしても値は変わらない
  • sをソートしておくとよさそう
  • 昇順ソートして考えるけどi番目を順列の先頭に入れるみたいになって嬉しくない
  • 降順ソートすれば順列の最後に入るのでよさそう
  • dp[k][j%a[i]] += dp[i][j] (i<k) に遷移すれば書けるけど明らかにTLE
  • i番目が値に影響しないときの遷移を入れればいい
  • 使わない場合a[i]を順列の後ろのどこかに挿入する
  • 挿入する場所はn-1-i通りあるので dp[i+1][j] += dp[i][j] * (n-1-i) とかける
  • 使う場合も前一箇所だけ見ればよくなるので dp[i+1][j%s[i]] += dp[i][j] でかける
  • 遷移がO(1)なので間に合う
#include <bits/stdc++.h>

using namespace std;
using ll = long long;
// #define int ll
using PII = pair<ll, ll>;

#define FOR(i, a, n) for (ll i = (ll)a; i < (ll)n; ++i)
#define REP(i, n) FOR(i, 0, n)
#define ALL(x) x.begin(), x.end()

template<typename T> T &chmin(T &a, const T &b) { return a = min(a, b); }
template<typename T> T &chmax(T &a, const T &b) { return a = max(a, b); }
template<typename T> bool IN(T a, T b, T x) { return a<=x&&x<b; }
template<typename T> T ceil(T a, T b) { return a/b + !!(a%b); }

template<typename T> vector<T> make_v(size_t a) { return vector<T>(a); }
template<typename T,typename... Ts>
auto make_v(size_t a,Ts... ts) {
    return vector<decltype(make_v<T>(ts...))>(a,make_v<T>(ts...));
}
template<typename T,typename V> typename enable_if<is_class<T>::value==0>::type
fill_v(T &t, const V &v) { t=v; }
template<typename T,typename V> typename enable_if<is_class<T>::value!=0>::type
fill_v(T &t, const V &v ) { for(auto &e:t) fill_v(e,v); }

template<class S,class T>
ostream &operator <<(ostream& out,const pair<S,T>& a){
    out<<'('<<a.first<<','<<a.second<<')'; return out;
}
template<class T>
ostream &operator <<(ostream& out,const vector<T>& a){
    out<<'['; for(T i: a) {out<<i<<',';} out<<']'; return out;
}

int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0}; // DRUL
const int INF = 1<<30;
const ll LLINF = 1LL<<60;
const ll MOD = 1000000007;

struct mint {
    ll x;
    mint(): x(0) { }
    mint(ll y) : x(y>=0 ? y%MOD : y%MOD+MOD) {}
    ll get() const { return x; }
    // e乗
    mint pow(ll e) {
        ll a = 1, p = x;
        while(e > 0) {
            if(e%2 == 0) {p = (p*p) % MOD; e /= 2;}
            else {a = (a*p) % MOD; e--;}
        }
        return mint(a);
    }
    // Comparators
    bool operator <(mint b) { return x < b.x; }
    bool operator >(mint b) { return x > b.x; }
    bool operator<=(mint b) { return x <= b.x; }
    bool operator>=(mint b) { return x >= b.x; }
    bool operator!=(mint b) { return x != b.x; }
    bool operator==(mint b) { return x == b.x; }
    // increment, decrement
    mint operator++() { x++; return *this; }
    mint operator++(signed) { mint t = *this; x++; return t; }
    mint operator--() { x--; return *this; }
    mint operator--(signed) { mint t = *this; x--; return t; }
    // Basic Operations
    mint &operator+=(mint that) {
        x += that.x;
        if(x >= MOD) x -= MOD;
        return *this;
    }
    mint &operator-=(mint that) {
        x -= that.x;
        if(x < 0) x += MOD;
        return *this;
    }
    mint &operator*=(mint that) {
        x = (ll)x * that.x % MOD;
        return *this;
    }
    mint &operator/=(mint that) {
        x = (ll)x * that.pow(MOD-2).x % MOD;
        return *this;
    }
    mint &operator%=(mint that) {
        x = (ll)x % that.x;
        return *this;
    }
    mint operator+(mint that) const { return mint(*this) += that; }
    mint operator-(mint that) const { return mint(*this) -= that; }
    mint operator*(mint that) const { return mint(*this) *= that; }
    mint operator/(mint that) const { return mint(*this) /= that; }
    mint operator%(mint that) const { return mint(*this) %= that; }
};
// Input/Output
ostream &operator<<(ostream& os, mint a) { return os << a.x; }
istream &operator>>(istream& is, mint &a) { return is >> a.x; }

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

    ll n, x;
    cin >> n >> x;
    vector<ll> s(n);
    REP(i, n) cin >> s[i];
    sort(ALL(s), greater<>());

    auto dp = make_v<mint>(n+1, 100001);
    dp[0][x] = 1;
    REP(i, n) REP(j, 100001) {
        dp[i+1][j%s[i]] += dp[i][j];
        dp[i+1][j] += dp[i][j] * (n-1-i);
    }

    mint ret = 0;
    REP(i, 100001) ret += dp[n][i] * i;
    cout << ret << endl;

    return 0;
}