アナログ2019年6月29日] [NOI2019組合せの数(ルーカス定理桁DP)

説明:


p <= 10、pは素数であり、n <= 7、L、R <= 1E18

ソリューション:


ルーカスの定理:

\(C_ {N} ^ M = C_ {N〜MOD〜P} ^ {M〜MOD〜P} * C_ {N / P} ^ {M / P} \)

場合\(N、Mの\) P-スケールで分解し、それは(\ {N-のProd C_ [I] ^ {M} [I]} \)\します

以下のための\(∈[L、R] \) 制限導入が却下\を(<= R&LT \)

ローからハイに桁のDPを考慮し、設定([I] [S fは\を ] [J] \) ビットの前に行わ表し、I、S [i]はi番目の数である選択<=または>にjビットの係数、および。

転送カウントそれぞれを列挙することができた場合は、この1はもちろん、<=または>、もちろん、これはまだ非常に遅い列挙することが何であるかを、選びました。

一つのDPを転送したい場合、セット次いで\(G [I] [S ] [j]は\) iは前に考えの数を表し、今形圧力状態がSであり、これは、Jの一つであり、初期値であります\(G [0] [S] [J] = F [I] [S] [J] \)

次いで、総時間の複雑さは約ある(O(2 ^ N * \ 2)\ ^ log_p ^ M * 2 ^ n個の*(PN))

とにかく、轢か。

コード:


#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, B = y; i <= B; i ++)
#define ff(i, x, y) for(int i = x, B = y; i <  B; i ++)
#define fd(i, x, y) for(int i = x, B = y; i >= B; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

int jx[11][11];
int n, p;
ll m, l[101], r[11], a[11];
int b[101], b0, c[11][101], c0[11];
int a2[10];
int ans;
int f[2][1 << 7][8], o;
int g[2][1 << 7][60], o2;

#define mem(a) memset(a, 0, sizeof a)
void dp(int xs) {
    mem(c);
    fo(i, 1, n) {
        ll v = a[i];
        c0[i] = 0;
        for(; v > 0; v /= p) c[i][++ c0[i]] = v % p;
    }
    mem(f); f[o][0][0] = 1;
    fo(i, 1, b0) {
        mem(f[!o]);
        mem(g);
        ff(j, 0, a2[n]) fo(k, 0, n - 1) g[o2][j][k] = f[o][j][k];
        fo(j, 1, n) {
            mem(g[!o2]);
            ff(s, 0, a2[n]) fo(k, 0, 48) if(g[o2][s][k]) {
                g[o2][s][k] %= p;
                int s2 = s & (a2[n] - 1 - a2[j - 1]); 
                int ns = s2;
                int l = 0, r = c[j][i] - 1;
                fo(u, l, r) g[!o2][ns][k + u] += g[o2][s][k];
                ns = s;
                l = r = c[j][i];
                g[!o2][ns][k + l] += g[o2][s][k];
                ns = s2 + a2[j - 1];
                l = c[j][i] + 1, r = p - 1;
                fo(u, l, r) g[!o2][ns][k + u] += g[o2][s][k];
            }
            o2 = !o2;
        }
        ff(s, 0, a2[n]) fo(k, 0, 48) {
            f[!o][s][k / p] += g[o2][s][k] * jx[b[i]][k % p];
        }
        ff(s, 0, a2[n]) fo(k, 0, p - 1) f[!o][s][k] %= p;
        o = !o;
    }
    ff(s, 0, a2[n]) {
        int ky = 1;
        fo(j, 1, n) if((s >> (j - 1) & 1) && c0[j] <= b0) { ky = 0; break;}
        if(ky) ans = (ans + f[o][s][0] * xs) % p;
    }
}

void dg(int x, int xs) {
    if(x > n) {
        dp(xs);
        return;
    }
    a[x] = l[x] - 1; dg(x + 1, -xs);
    a[x] = r[x]; dg(x + 1, xs);
}

int main() {
    freopen("combination.in", "r", stdin);
    freopen("combination.out", "w", stdout);
    fo(i, 0, 7) a2[i] = 1 << i;
    scanf("%d %lld %d", &n, &m, &p);
    fo(i, 0, 10) {
        jx[i][0] = 1;
        fo(j, 1, i) jx[i][j] = (jx[i - 1][j - 1] + jx[i - 1][j]) % p;
    }
    fo(i, 1, n) scanf("%lld %lld", &l[i], &r[i]);
    for(; m; m /= p) b[++ b0] = m % p;
    dg(1, 1);
    ans = (ans % p + p) % p;
    pp("%d\n", ans);
}

おすすめ

転載: www.cnblogs.com/coldchair/p/11110267.html