Good Bye 2017 部分題解

D. New Year and Arbitrary Arrangement

分析

\(dp[i][j]\) 表示已有 \(i\)\(a\)\(j\)\(ab\) 的狀況下繼續構造能獲得的 \(ab\) 個數的指望。c++

考慮 DFS 記憶化搜索。spa

有兩個要注意的地方:
\(p_a\) 爲添加 \(a\) 的機率,\(p_b\) 爲添加 \(b\) 的機率。code

  1. \(i + j \geq k\) 時,這個狀況下添加一個 \(b\) 構造就中止了,可是在這個 \(b\) 以前顯然能夠無限添加 \(a\) ,這後面的指望爲 \((i + j) * p_b + p_a*(i+j+1)*p_b+p_a^2*(i+j+2)*p_b+...\) 這個式子能夠化簡,\(O(1)\) 計算。
  2. 起始狀態應該是 \(dp[0][0] = dp[1][0] * p_a + dp[0][0] * p_b\)\(dp[0][0] * p_b\) 說明咱們能夠不斷添加前導 \(b\) 。能夠發現 \(dp[0][0] = (p_b^0+p_b^1+p_b^2...) * dp[1][0] * p_a=\frac{1}{1-p_b}*dp[1][0]*p_a=dp[1][0]\),因此咱們能夠直接計算一個以 \(a\) 開頭的序列 ,即計算 \(dp[1][0]\)

code

#include<bits/stdc++.h>
using namespace std;

const int MOD = 1e9 + 7;

long long POW(long long x, int k) {
    long long ret = 1;
    while(k) {
        if(k & 1) ret = (ret * x) % MOD;
        x = x * x % MOD;
        k >>= 1;
    }
    return ret;
}

long long k, pa, pb, dp[1001][1001];

long long dfs(int a, int ab) {
    if(a + ab >= k) return a + ab + pa * POW(pb, MOD - 2) % MOD;
    if(dp[a][ab] != -1) return dp[a][ab];
    return dp[a][ab] = (dfs(a + 1, ab) * pa % MOD + dfs(a, ab + a) * pb % MOD) * POW(pa + pb, MOD - 2) % MOD;
}

int main() {
    memset(dp, -1, sizeof dp);
    cin >> k >> pa >> pb;
    long long p = __gcd(pa, pb);
    pa /= p;
    pb /= p;
    cout << dfs(1, 0) << endl;
    return 0;
}

E. New Year and Entity Enumeration

分析

官方題解 很詳細了,本身補充幾點。blog

  1. 考慮 \(m\) 個二進制數,假設 \(m=4\) ,那麼有 0001,0010,0100,1000 。這些數必定是某一二進制位有 \(1\) 的最小的數,用 \(f\) 表示這個關係。\(f[0]=0001\)\(f[1]=0010\) ... 。這些數中任意兩個數按位與後都爲 \(0\) ,那麼能夠考慮求這個集合劃分方法的數目,即貝爾數 ,將各個子集中的數分別按位或起來,舉個例子 1000,0100,0011 。這種狀況下,第一位和第二位爲 \(1\) 的二進制數都是 0011 ,能夠發現,各類劃分方案中咱們獲得的 \(f\) 並不會徹底相同,因此用這個劃分方案數就對應着 \(m\) 位二進制數的解(具體可見官方題解的證實)。
  2. 題目要求 \(T\)\(S\) 的子集。以樣例爲例,
    11010
    00101
    11000
    豎着去看,有兩個 101 兩個 010 一個 100 。考慮兩個 101 ,咱們再橫着去看,即 00 11 00 ,咱們知道一個 \(k\) 進制數的任意一種方案必定包括 \(k\)\(0\)\(k\)\(1\) 這兩個二進制數,咱們分開去求必定能夠獲得知足題目的方案,若 \(b[i]\) 爲貝爾數,那麼這個答案就是 \(b[2] * b[2] * b[1]\)

code

#include<bits/stdc++.h>
using namespace std;
const int N = 1e3 + 10;
const int MOD = 1e9 + 7;
long long C[N][N];
map<long long, int> mp;
long long a[N], b[N], c[N][N];
int main() {
    for(int i = 0; i < N; i++) {
        C[i][0] = 1;
        for (int j = 1; j <= i; j++) {
            C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % MOD;
        }
    }
    int n, m;
    cin >> m >> n;
    while(n--) {
        long long y = 0;
        for (int i = 0; i < m; i++) {
            long long x;
            scanf("%1lld", &x);
            a[i] |= x << n;
        }
    }
    for (int i = 0; i < m; i++) {
        mp[a[i]]++;
    }
    b[0] = b[1] = 1;
    for (int i = 2; i <= m; i++) {
        for (int j = 0; j < i; j++) {
            (b[i] += C[i - 1][j] * b[i - j - 1]) %= MOD;
        }
    }
    long long ans = 1;
    for(auto it : mp) {
        (ans *= b[it.second]) %= MOD;
    }
    cout << ans << endl;
    return 0;
}

F. New Year and Rainbow Roads

分析

以 G 爲間隔分組,每一個組內單獨討論,要知足題目條件有兩種連法:ip

  1. 兩個 G 不直接相連,經過鏈接 B 間接連上,也要經過鏈接 R 間接連上,花費爲兩倍兩個 G 直接的距離。
  2. 兩個 G 直接相連,那麼 B 所有連上的話就成了一個環了,考慮去掉花費最大的一條邊,R 相似。

code

#include<bits/stdc++.h>
using namespace std;

int preg, prer, preb, fr, lr, fb, lb, mxr, mxb;
int main() {
    int n; 
    cin >> n;
    long long ans = 0;
    for (int i = 0; i < n; i++) {
        int p;
        char c[2];
        scanf("%d%s", &p, c);
        if(c[0] == 'G') {
            if(prer) mxr = max(mxr, p - prer);
            if(preb) mxb = max(mxb, p - preb);
            if(!preg) { // 第一個 G 前面
                if(fr) {
                    ans += p - fr;
                } 
                if(fb) {
                    ans += p - fb;
                }
            } else { // 兩個 G 之間
                ans += min(2LL * (p - preg), 3LL * (p - preg) - mxb - mxr);    
            }
            prer = p;
            preb = p;
            preg = p;
            lb = lr = p;
            mxb = mxr = 0;
        } else if(c[0] == 'B') {
            if(!fb) fb = p;
            if(preb) mxb = max(mxb, p - preb);
            preb = p;
            lb = p;
        } else if(c[0] == 'R') {
            if(!fr) fr = p;
            if(prer) mxr = max(mxr, p - prer);
            prer = p;
            lr = p;
        }
    }
    if(!preg) {
        if(lr != fr) ans += lr - fr;
        if(lb != fb) ans += lb - fb;
    } else {
        ans += lr - preg;
        ans += lb - preg;
    }
    cout << ans << endl;
    return 0;
}

G. New Year and Original Order

分析

\(dp[i][j][k][o]\) 表示前 \(i\) 個數中有 \(j\) 個大於等於 \(k\) 的數時的構數方案數,\(o\) 爲了保證邊界,構造的數不大於 \(X\)ci

舉個例子,比方說一個數 \(3312\) 咱們要累積這個數對答案的貢獻,其實是 \(1233\) ,大於等於 \(1\) 的數有 \(4\) 個,咱們能夠認爲貢獻了 \(1111\) ,大於等於 \(2\) 的數有 \(3\) 個,貢獻了 \(111\) ,大於等於 \(3\) 的數有 \(2\) 個,貢獻了 \(11\)get

最後枚舉 \(k\),累計計算一下答案便可。it

code

#include<bits/stdc++.h>
using namespace std;

const int MOD = 1e9 + 7;
const int N = 707;

char s[N];
int dp[N][N][10][2];

int main() {
    scanf("%s", s + 1);
    int n = strlen(s + 1);
    for (int i = 1; i < 10; i++) {
        dp[0][0][i][0] = 1;
    }
    for (int i = 0; i < n; i++) {
        for (int j = 0; j <= i; j++) {
            for (int k = 1; k < 10; k++) {
                for (int o = 0; o < 2; o++) {
                    for (int p = 0; p <= (!o ? s[i + 1] - '0' : 9); p++) {
                        (dp[i + 1][j + (p >= k)][k][o | p < s[i + 1] - '0'] += dp[i][j][k][o]) %= MOD;
                    }
                }
            }
        }
    }
    long long ans = 0;
    for(int k = 1; k < 10; k++) {
        for (long long i = 1, p = 1; i <= n; i++, p = (p * 10 + 1) % MOD) {
            (ans += p * (dp[n][i][k][0] + dp[n][i][k][1]) % MOD) %= MOD;
        }
    }
    printf("%I64d\n", ans);
    return 0;
}
相關文章
相關標籤/搜索