P7740-[NOI2021]机器人游戏【dp,bitset】
正题
题目链接:https://www.luogu.com.cn/problem/P7740
题目描述
题目大意摸了
小 R 有 \(m\)(\(1 \le m \le 1000\))个机器人和 \(m\) 张纸带,第 \(i\)(\(1 \le i \le m\))个机器人负责对第 \(i\) 张纸带进行操作。对于每张纸带,它们都被从左到右分成了 \(n\)(\(1 \le n \le 32\))个格子,依次编号为 \(0, 1, \ldots , n - 1\)。每个格子有 \(3\) 种状态:1. 格子上写有数字 \(0\);2. 格子上写有数字 \(1\);3. 格子是一个空格子。
在任意时刻,机器人必须站在纸带上的一个格子中。在设定好机器人在纸带上的初始位置后,第 \(i\) 个机器人会依次执行预先设定的操作序列 \(S_i\),操作由 R
、0
、1
、*
四种字符组成,其中:
R
表示机器人向右走一格,如果右边没有格子,则机器人会原地爆炸;0
表示如果机器人所在格子非空,则将该格子上的数字改为 \(0\),否则不修改;1
表示如果机器人所在格子非空,则将该格子上的数字改为 \(1\),否则不修改;*
表示如果机器人所在格子非空,则将格子上的数字 \(x\) 改为 \(1 - x\),否则不修改。
第 \(i\) 张纸带的状态可以用一个长度为 \(n\) 的序列表示,每个元素为 0
、1
或 -
(空格子),依次表示其每个格子的状态。第 \(i\) 张纸带的初始状态称为机器人 \(i\) 的输入 \(X_i\),操作执行完成后纸带的状态称为机器人 \(i\) 的输出 \(Y_i\)。注意,如果机器人爆炸了,那么这个机器人就没有输出。
可以发现,如果一个格子为空,那么机器人永远不会修改它。所以每个机器人都有如下特性:如果第 \(i\) 个机器人所在的纸带上的所有格子都为空,那么它就不会执行任何操作,它的输出即为所有格子都为空。
现在小 R 给定了每一个机器人的输入 \(X_i\)(即每张纸带的初始状态)以及目标输出 \(Y_i\)。小 R 希望小 D 找到一个位置 \(p\)(\(0 \le p < n\)),使得所有机器人都能以其所在纸带的第 \(p\) 个格子为初始位置,在不爆炸的情况下执行完所有操作,并且满足第 \(i\) 个机器人的输出为 \(Y_i\)。
小 D 花了几毫秒解决了问题,现在他想知道,有多少个输入和输出的组合方式使得上述问题有解,即有多少种为每个机器人设定输入 \(X_0, X_1, \ldots , X_{m - 1}\) 和目标输出 \(Y_0, Y_1, \ldots , Y_{m - 1}\) 的方式,使得至少存在一个位置 \(p\)(\(0 \le p < n\)),使得所有机器人都能以其所在纸带的第 \(p\) 个格子为起点,在不爆炸的情况下执行完所有操作,且满足第 \(i\) 个机器人的输出为 \(Y_i\)。请你帮助小 D 解决这个问题,由于最终的答案可能很大,请你输出答案对 \({10}^9 + 7\) 取模后的余数。
两个组合方式不同当且仅当,存在至少一个机器人,它的输入或是目标输出在两个方式中不同。
对于所有测试点:\(1 \le n \le 32\),\(1 \le m \le 1000\),\(1 \le \lvert S_i \rvert \le 100\)。
测试点编号 | \(n \le\) | \(m \le\) | 特殊限制 |
---|---|---|---|
\(1 \sim 2\) | \(1\) | \(1\) | 无 |
\(3\) | \(8\) | \(1\) | 无 |
\(4\) | \(16\) | \(1\) | 无 |
\(5 \sim 6\) | \(32\) | \(1\) | 无 |
\(7\) | \(16\) | \(5\) | 无 |
\(8 \sim 10\) | \(32\) | \(5\) | 无 |
\(11 \sim 12\) | \(16\) | \(1000\) | 无 |
\(13 \sim 15\) | \(32\) | \(1000\) | A |
\(16 \sim 21\) | \(32\) | \(1000\) | B |
\(22 \sim 25\) | \(32\) | \(1000\) | 无 |
特殊限制 A:操作序列中不存在 R
。
特殊限制 B:每个操作序列中,R
的数量至多 \(15\) 个。
解题思路
先考虑\(n\)比较小的情况。对于每个机器人,我们可以把它的操作表示成相对于起点位置的4种状态:必定为\(0/1\),与之前相同/不同。
然后至少有一个合法的\(x\)的限制我们可以考虑容斥,钦定一些合法的位置,那么对于纸带的一个位置来说它的限制最多只有四种,我们对于不同的情况这个位置不同的起始状态对应同样数量的中止状态。
但是我们暴力枚举合法位置的复杂度是\(O(2^n)\)的,考虑能不能优化。
因为这个\(n=32\),看上去就很像折半,所以我们考虑一下怎么折半。
我们会发现因为最大位置不能超出纸条,所以设一个机器人最远的操作位置是\(len\),那么对于这个机器人来说合法的\(x\)就不能超过\(n-len\)。而如果\(len\)比较小,那么一个位置能影响到后面的位置就不多,我们可以设\(f_{i,S}\)表示目前做到\(i\),对于后面的限制目前是\(S\)时前面已经确定了限制的位置的答案。
然后所有的机器人同时做就可以了。
然后再处理限制的时候用\(bitset\)优化一下就行了。
时间复杂度:\(O(\frac{2^nnm}{\omega})\)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<bitset>
#define ll long long
using namespace std;
const ll N=34,M=1100,P=1e9+7;
ll n,m,pw2[M],pw3[M],v[M][N],c[M];
ll ans,num,f[N][1<<17][2];
vector<ll> q[N];char s[N*3];
bitset<M> all,v1,v2,u_[N],u[4][N],F[1<<17][4];
signed main()
{
scanf("%lld%lld",&n,&m);
pw2[0]=pw3[0]=1;
for(ll i=1;i<=m;i++)pw2[i]=pw2[i-1]*2ll%P,pw3[i]=pw3[i-1]*3ll%P;
for(ll i=0;i<m;i++){
scanf("%s",s+1);ll len=strlen(s+1),cr=0;
for(ll j=1;j<=len;j++){
if(s[j]=='R')v[i][c[i]]=cr,cr=0,c[i]++;
else if(s[j]=='0')cr=2;
else if(s[j]=='1')cr=3;
else if(s[j]=='*')cr^=1;
}
v[i][c[i]]=cr;q[c[i]].push_back(i);
all.set(i);
}
ans=1;
for(ll i=1;i<=n*m;i++)ans=ans*3ll%P;
for(ll r=n;r>=0;r--){
for(ll i=0;i<q[n-r].size();i++){
for(ll j=0;j<n+2;j++)u[v[q[n-r][i]][j]][j].set(q[n-r][i]);
for(ll j=c[q[n-r][i]]+1;j<n+2;j++)u_[j].set(q[n-r][i]);
}
f[0][0][0]=1;num+=q[n-r].size();
for(ll i=1;i<=r;i++){
ll MS=(1<<min(i,n-r+1)),MT=(1<<min(i-1,n-r+1));
for(ll s=0;s<MS;s++)f[i][s][0]=f[i][s][1]=0;
for(ll z=0;z<2;z++)
for(ll s=0;s<MT;s++){
ll _z=z|(s*2>=MS);
if(i<r)(f[i][(s<<1)&(MS-1)][_z]+=f[i-1][s][z])%=P;
(f[i][(s<<1|1)&(MS-1)][_z]+=P-f[i-1][s][z])%=P;
}
for(ll z=0;z<2;z++){
ll lr=z|(i<r);
for(ll s=0;s<min(n-r+1,i);s++){
for(ll j=lr;j<4;j++)F[1<<s][j]=u[j][s];
if(!lr)F[1<<s][0]|=u_[s];
}
for(ll s=0;s<MS;s++){
if(s!=(s&-s)){
for(ll k=lr;k<4;k++)
F[s][k]=F[s&-s][k]|F[s-(s&-s)][k];
}
if(lr){
v1=F[s][1]|(F[s][2]&F[s][3]);
v2=(F[s][2]|F[s][3])&(v1^all);
}
else{
v1=(F[s][0]&F[s][1])|(F[s][2]&F[s][3]);
v2=(F[s][0]|F[s][1])&(F[s][2]|F[s][3])&(v1^all);
}
ll X=v1.count(),Y=v2.count();
(f[i][s][z]=f[i][s][z]*pw2[Y]%P*pw3[num-X-Y]%P)%=P;
}
}
}
ll lim=min(n-r+1,r),MS=1<<lim;
for(ll z=0;z<2;z++)
for(ll i=1;i<=n-r;i++){
for(ll s=0;s<lim;s++){
for(ll j=z;j<4;j++)F[1<<s][j]=u[j][s+i];
}
for(ll s=0;s<MS;s++){
if(s!=(s&-s)){
for(ll k=z;k<4;k++)
F[s][k]=F[s&-s][k]|F[s-(s&-s)][k];
}
if(z){
v1=F[s][1]|(F[s][2]&F[s][3]);
v2=(F[s][2]|F[s][3])&(v1^all);
}
else{
v1=(F[s][0]&F[s][1])|(F[s][2]&F[s][3]);
v2=(F[s][0]|F[s][1])&(F[s][2]|F[s][3])&(v1^all);
}
ll X=v1.count(),Y=v2.count();
(f[r][s][z]=f[r][s][z]*pw2[Y]%P*pw3[num-X-Y]%P)%=P;
}
}
for(ll z=0;z<2;z++)
for(ll s=0;s<MS;s++)
(ans+=P-f[r][s][z])%=P;
}
printf("%lld\n",ans);
return 0;
}