trick:相对位置关系 可以插入计数
直接D1 D2一起讲解
先简单看D1 也就是没有问号的时候
那么对w的每个字符 如果是1 就有有一个子数组mex=i
反之就不能有子数组mex=i;
首先对于单个0 以及整个区间的情况 一定会有mex=1 以及mex=n;
因此w1=wn=1;
其次我们计算方案数 [插入计数]
我们可以通过插入0到n-1这些数字来构造一个合法排列
刚开始插入0 当0到k-1已经定了的时候 我们要插入k
若wk=1 那么k能插入的位置只有整个已插入数字的左右两侧 只有这样才能保证有mex=k存在
若wk=0 那么k可以插入到已经插入的相邻数字之间 已经插入了k个数字 那就有k-1个位置可以插入 这样的话当区间选到k的时候 mex不可能为k
也就是wi=1 就乘2 wi=0 就乘i;
由于取模 所以判断ans与c是否整除的时候不能最后除法判断 要在每个乘法的过程 用c/(gcd(c,x))x表示ans的因子 也就是上述乘法过程中的每个数
对于D2部分 ?可以为1 可以为0
我们要保证答案不被c整除并且答案尽可能小
那么非?部分已经确定 我们用c除以固定部分的公因数后得到c1 那么c1与剩下部分的乘积不能整除
想要尽可能小 我们肯定要尽可能选择乘2 而不是乘i
当我们全选择乘2的时候那么乘积就是一个2的次幂 如果c不是二的次幂 完全就可以这么贪心的选择
当c是二的次幂的时候 假设有x个? 那么就是x次幂 假设c是k次幂 我们要尽可能让x<k 这样就不会整除 也就是要选择问号 变为0 乘i 如果i是偶数 那么天然带2 无法降低2的次幂 我们只能选择奇数变成0 偶数必须全变成2 我们选择较小的奇数变为0 这样的话 乘的i尽可能小 答案也小
代码如下:
cpp
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 1e9+7;
void solve() {
int n, c;
cin >> n >> c;
string s;
cin >> s;
if (s[1] == '?') {
s[1] = '0';
}
if (s[0] == '0' || s[n-1] == '0') {
cout << -1;
return;
}
s[0] = '1';
s[n-1] = '1';
vector<int> good;
int cnt = 0;
for (int i = 0; i < n-1; ++i) {
if (s[i] == '0') {
c /= gcd(c, i);
} else if (s[i] == '1') {
c /= gcd(c, 2ll);
}else {
cnt++;
if (i % 2 == 1) {
good.push_back(i);
}
}
}
if (c != (1 << __lg(c))) {
int ans = 1;
for (int i = 0; i < n-1; ++i) {
if (s[i] == '0') {
ans *= i;
}else {
ans *= 2;
}
ans %= mod;
}
cout << ans;
return;
}
int w = cnt;
int h = __lg(c);
for (int i = 0; i < good.size(); ++i) {
if (w < h) {
break;
} else {
s[good[i]] = '0';
w--;
}
}
if (w >= h) {
cout << -1;
return;
}
int ans = 1;
for (int i = 0; i < n-1; ++i) {
if (s[i] == '0') {
ans *= i;
}else {
ans *= 2;
}
ans %= mod;
}
cout << ans;
}
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int t = 1;
cin >> t;
while (t--) {
solve();
cout << '\n';
}
}