【CF804F]偽地金

1.png

溶液:

この問題は、二つの部分に分けることができ、

バーの最大数やバーの最小数の各点の部分を処理する、と呼ばれる\([Min_i、MAX_I] \ )

第二の部分は、のためのものである\(N- \)変数([min_i、MAX_I]でX_I \ \カップ\はZ {} \ mathbb)\、算出選択\(B \)をフロント番目の\(\)ビッグ変数プログラムの数。

二つの点に関してはu->v、あればu人はi (棒持っている)v人々j満たすために\(I \当量J(\ {テキスト} MOD \ GCD(S_Uは、S_V))\) それはi与えることができますj偽の棒を。

同様に、パスのu->...->v提供gのために\(\ GCD \) 次いで限り\(I \当量J(\ {テキスト} MOD G)が\) それをi与えることができるj偽バー。

そう強連結成分のために、金は、人々が満足するバー(\)I \ J当量(\ {テキスト} MOD G)\をj各点の人のための金バー、g強連結成分の全体のための(\ \ GCD \)

あなたができるように列挙金は、ポイントの人物バー\(O(\テキスト{番号}バーが)\)強連結成分のバーを得るために、それぞれの状態を持っています。

トーナメントの性質によって、トーナメントや競争ポイント還元図、およびこのような社長:

2.png

どうやら私たちは金塊の数を最大化することを可能にするためには、その背後側に接続された各点はその仮定の地金数の各強連結成分について計算ポイントを数え貢献今度はトポロジカル順序に従っているMx各点がuありバーの数である(\ FRAC S_uMx} {G} {\)\します

次に、カウントは、しないかを再検討する列挙u極小点Bの場合、統計\(Max_u <Min_i \)\(CNT_1 \) および\(MAX_I \ GEQ Max_u \ GEQ Min_iを\)番号\(CNT_2 \) 再度列挙し\(CNT_2 \)選択されたj数、追加の答は\({〜cnt_2〜\選択 Jを} {J - - 1〜cnt_1〜\ Bを選択します} \) 。

#include <iostream>
#include <cstdio>
#include <set>
#include <algorithm>
#include <vector>
#define LL long long
using namespace std;
const int maxn = 5003;
const int MOD = 1e9 + 7;
vector<int> g[maxn];
vector<bool> city[maxn];
int A, B, n;
int s[maxn], fac[maxn], ifac[maxn];
void input() {
    char str[(int)(2e6) + 2];
    scanf("%d %d %d", &n, &A, &B);
    for (int i = 1; i <= n; ++i) {
        scanf("%s", str + 1);
        for (int j = 1; j <= n; ++j) 
            if (str[j] == '1') 
                g[i].push_back(j);
    }
    for (int i = 1; i <= n; ++i) {
        scanf("%d %s", &s[i], str);
        city[i].resize(s[i]);
        for (int j = 0; j < s[i]; ++j) 
            city[i][j] = str[j] - '0';
    }
}
LL qpow(LL a, LL b) {
    LL res(1);
    while (b) {
        if (b & 1) {
            res = res * a % MOD;
        }
        a = a * a % MOD;
        b >>= 1;
    }
    return res;
}
void init() {
    fac[0] = 1;
    int N = maxn - 3;
    for (int i = 1; i <= N; ++i) 
        fac[i] = 1ll * fac[i - 1] * i % MOD;
    ifac[N] = qpow(fac[N], MOD - 2);
    for (int i = N - 1; i >= 0; --i) 
        ifac[i] = 1ll * ifac[i + 1] * (i + 1) % MOD;
}
vector<bool> colbull[maxn];
int Gcdcol[maxn], cntbull[maxn], maxbull[maxn], minbull[maxn];
int low[maxn], dfn[maxn], dfst, col[maxn], colcnt, stk[maxn], top;
void tarjan(int u) {
    dfn[u] = low[u] = ++dfst;
    stk[++top] = u;
    for (int i = 0; i < (int)g[u].size(); ++i) {
        int v= g[u][i];
        if (!dfn[v]) {
            tarjan(v);
            low[u] = min(low[u], low[v]);
        } else if (!col[v]) {
            low[u] = min(low[u], dfn[v]);
        }
    }
    if (low[u] == dfn[u]) {
        ++colcnt;
        int v;
        do {
            v = stk[top--];
            col[v] = colcnt;
        } while (u != v);
        Gcdcol[colcnt] = s[u];
    }
}
void solve1() {
    for (int i = 1; i <= n; ++i)
        if (!dfn[i]) 
            tarjan(i);
    for (int i = 1; i <= n; ++i) 
        Gcdcol[col[i]] = __gcd(Gcdcol[col[i]], s[i]);
    for (int i = 1; i <= n; ++i)  {
        colbull[col[i]].resize(Gcdcol[col[i]]);
        for (int j = 0; j < s[i]; ++j)  
            if (city[i][j] == 1) {
                colbull[col[i]][j % Gcdcol[col[i]]] = 1;
            }
    }
    vector<bool> tmp;
    for (int i = colcnt; i >= 2; --i) {
        int g = __gcd(Gcdcol[i], Gcdcol[i - 1]);
        tmp.clear();
        tmp.resize(g);
        for (int j = 0; j < Gcdcol[i]; ++j)
            tmp[j % g] = tmp[j % g] | colbull[i][j];
        for (int j = 0; j < Gcdcol[i - 1]; ++j)
            colbull[i - 1][j] = colbull[i - 1][j] | tmp[j % g];
    }
    for (int i = 1; i <= colcnt; ++i) 
        for (int j = 0; j < Gcdcol[i]; ++j)
            cntbull[i] += colbull[i][j];
    for (int i = 1; i <= n; ++i)
        maxbull[i] = s[i] / Gcdcol[col[i]] * cntbull[col[i]];
    for (int i = 1; i <= n; ++i)
        for (int j = 0; j < s[i]; ++j)
            minbull[i] += city[i][j];
}
LL ans;
LL combine(int n, int m) {
    if (m < 0 || n < 0 || m > n) return 0;
    return 1ll * fac[n] * ifac[m] % MOD * ifac[n - m] % MOD;
}
void solve2() {
    for (int i = 1; i <= n; ++i) {
        int cnt1 = 0, cnt2 = 0;
        for (int j = 1; j <= n; ++j) {
            if (i == j) continue;
            if (minbull[j] > maxbull[i]) ++cnt1;
            else if (maxbull[j] > maxbull[i] || (maxbull[j] == maxbull[i] and j < i)) ++cnt2;
        }
        if (cnt1 >= A) continue;
        for (int j = min(B - 1, min(cnt2, A - 1 - cnt1)); j >= B - cnt1 - 1 && j >= 0; j--) {
            ans = (1ll * ans + 1ll * combine(cnt1, B - j - 1) * combine(cnt2, j) % MOD) % MOD;
        }
    }
    cout << ans << endl;
}
int main() {
   // freopen("fake.in", "r", stdin);
   // freopen("fake.out", "w", stdout);
    input();
    init();
    solve1();
    solve2();
    return 0;
}

おすすめ

転載: www.cnblogs.com/cnyali-Tea/p/11439919.html