强智杯"2020年湖南省大学生计算机程序设计竞赛 C Absolute Difference Equation 题解
vp 出了7个题,死活想不出C,哎脑子还是不够用。
C Absolute Difference Equation
题意
对于一个长度为 的 字符串 ,一次操作为 。其中 。 对于一个给定的 字符串,某些位上为 ,可以任意填 ,求经过 次操作后剩下的唯一的数为 的初始数组的方案数。
题解
对于题目中的 ,可以视为 。 观察长度为 的 串经过 次操作后的数的结果。
时, 次操作后剩下的数为 。
时, 次操作后剩下的数位 。
时, 第一次操作后 。
第二次操作后 。
时, 第一次操作后 。
第 次操作后 。
计算一下每个位置的数出现在最终的数里的次数,
,
,
,
,
于是我们就发现,每个位置上的数在最终的表达式里出现的次数恰好和杨辉三角相同,也就是组合数学。
对于最终的答案是否为 ,我们只需要判断每个 出现在最终表达式中的次数之和的奇偶就行了。
这里给出组合数学奇偶性判断的结论: 对于 ,若 & ,则为奇数,否则为偶数。
于是我们就可以愉快的进行DP方程转移了,具体见代码及注释。
代码
#include <bits/stdc++.h>
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;
}