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;
}
相关文章
相关标签/搜索