“强智杯“2020年湖南省大学生计算机程序设计竞赛(C题)

vp 出了7个题,死活想不出C,哎脑子还是不够用。

比赛链接:强智杯"2020年湖南省大学生计算机程序设计竞赛

C Absolute Difference Equation

题意

对于一个长度为 n n n 0 / 1 0/1 0/1 字符串 a a a,一次操作为 f ( a ) = b 1 , b 2 , … , b n − 1 f(a) = b_1,b_2,\dots,b_{n-1} f(a)=b1,b2,,bn1。其中 b i = ∣ a i − a i + 1 ∣ b_i = \vert a_i - a_{i+1}\vert bi=aiai+1
对于一个给定的 0 / 1 0/1 0/1 字符串,某些位上为 ? ? ?,可以任意填 0 / 1 0/1 0/1,求经过 n − 1 n - 1 n1 次操作后剩下的唯一的数为 1 1 1 的初始数组的方案数。

题解

对于题目中的 b i = ∣ a i − a i + 1 ∣ b_i = \vert a_i - a_{i+1}\vert bi=aiai+1,可以视为 b i = a i ⨁ a i + 1 b_i = a_i\bigoplus a_{i+1} bi=aiai+1
观察长度为 1 , 2 , 3 , 4 1,2,3,4 1,2,3,4 0 / 1 0/1 0/1 串经过 n − 1 n - 1 n1 次操作后的数的结果。

n = 1 n = 1 n=1 时, n − 1 n - 1 n1 次操作后剩下的数为 a 1 a_1 a1

n = 2 n = 2 n=2时, n − 1 n - 1 n1 次操作后剩下的数位 a 1 ⨁ a 2 a_1\bigoplus a_2 a1a2

n = 3 n = 3 n=3 时,
第一次操作后 a ′ = a 1 ⨁ a 2 , a 2 ⨁ a 3 a' = a_1\bigoplus a_2, a_2\bigoplus a_3 a=a1a2,a2a3
第二次操作后 a ′ ′ = a 1 ⨁ a 2 ⨁ a 2 ⨁ a 3 a'' = a_1\bigoplus a_2\bigoplus a_2\bigoplus a_3 a′′=a1a2a2a3

n = 4 n = 4 n=4 时,
第一次操作后 a ′ = a 1 ⨁ a 2 , a 2 ⨁ a 3 , a 3 ⨁ a 4 a' = a_1\bigoplus a_2, a_2\bigoplus a_3,a_3\bigoplus a_4 a=a1a2,a2a3,a3a4
n − 1 n - 1 n1 次操作后 a ′ ′ ′ = a 1 ⨁ a 2 ⨁ a 2 ⨁ a 3 ⨁ a 2 ⨁ a 3 ⨁ a 3 ⨁ a 4 a''' = a_1\bigoplus a_2\bigoplus a_2\bigoplus a_3\bigoplus a_2\bigoplus a_3\bigoplus a_3\bigoplus a_4 a′′′=a1a2a2a3a2a3a3a4

计算一下每个位置的数出现在最终的数里的次数,
n = 1 n = 1 n=1 1 1 1
n = 2 n = 2 n=2 1 1 1\quad 1 11
n = 3 n = 3 n=3 1 2 1 1\quad 2\quad 1 121
n = 4 n = 4 n=4 1 3 3 1 1\quad 3\quad 3\quad 1 1331

于是我们就发现,每个位置上的数在最终的表达式里出现的次数恰好和杨辉三角相同,也就是组合数学。

对于最终的答案是否为 1 1 1,我们只需要判断每个 1 1 1 出现在最终表达式中的次数之和的奇偶就行了。

这里给出组合数学奇偶性判断的结论:
对于 C ( n , m ) C(n, m) C(n,m),若 n n n & m = m m = m m=m,则为奇数,否则为偶数。

于是我们就可以愉快的进行DP方程转移了,具体见代码及注释。

代码

#include 
using namespace std;
#define ll long long
const int N = 1e6 + 10, mod = 1e9 + 7;

char s[N];
int f[N][2][2]; // f[i][j][k]: 第 i 位为 0/1 且 前i个数中 1 出现在最终表达式中的个数为 0/1 的方案数

bool get(int n, int m){ // 判断组合数奇偶性
    return ((n & m) == m);
}
void solve(){
    int n = strlen(s + 1);
    
    for(int i = 0; i <= n; i ++){
        for(int j = 0; j < 2; j ++){
            for(int k = 0; k < 2; k ++) f[i][j][k] = 0;
        }
    }

    f[0][0][0] = 1;
    for(int i = 1; s[i]; i ++){
        int odev = get(n - 1, i - 1);

        // 当前为1或由?变成1,若当前位出现的次数为奇数,那么就能改变前i个的奇偶性,0 -> 1,1 -> 0,否则只能直接转移
        if(s[i] == '1' || s[i] == '?'){ 
            f[i][1][1] = (f[i - 1][1][odev ^ 1] + f[i - 1][0][odev ^ 1]) % mod;
            f[i][1][0] = (f[i - 1][1][odev ^ 0] + f[i - 1][0][odev ^ 0]) % mod;
        }

        // 当前为0或由?变成0,因为当前是0所以出现在最终表达式中的次数不影响结果,直接转移 0 -> 0,1 -> 1
        if(s[i] == '0' || s[i] == '?'){
            f[i][0][1] = (f[i - 1][1][1] + f[i - 1][0][1]) % mod;
            f[i][0][0] = (f[i - 1][1][0] + f[i - 1][0][0]) % mod;
        }
    }
    cout << (f[n][1][1] + f[n][0][1]) % mod << "\n"; // 最终为1的答案之和
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    while(cin >> (s + 1)){
        solve();
    }
    return 0;
}

你可能感兴趣的:(CPC,VP,DP,专栏,c++,动态规划)