[NOI2021] 机器人游戏
一、题目
二、解法
全程在 的帮助下补了这道题,果然他又帅又强👍
根据样例解释的提示,考虑对起点容斥。也就是枚举一个起点集合 ,求有多少种输入使得从 中的起点出发,得到的输出相同,然后带上 的容斥系数即可。
起点对位置 的限制只有 种,即 赋值0/赋值1/不变/取反
,可以直接分类讨论计算方案数:
- 如果走出去了,那么整个序列的格子贡献都是
- 如果同时拥有前两种或者后两种,那么 的贡献为
- 如果前两种有一个,后两种有一个,那么 的贡献为
- 如果只拥有四种中的一种,那么 的贡献为
暴力进行这个过程,对于每个机器人分开来计算,再用乘法原理即可得到答案,时间复杂度
考虑优化,主要矛盾在于 ,我们想办法用折半的方法优化它,枚举最后的起点 ,有这样的 :
- 若 ,可以直接暴力枚举 ,时间复杂度
- 若 ,那么需要考虑的序列必然满足步数
对于第二种情况我们考虑 ,设 表示现在考虑到了格子 ,和格子 距离 以内的选取情况是 ,在这个距离以外有无被选取的点(因为这会新增一个不变的限制),那么状态数是 的。转移考虑枚举当前格子是否作为起点被选取,如果被选取会增加 的容斥系数(即 计算容斥系数的技巧)
时间复杂度 ,现在的瓶颈在于 ,考虑不同序列的 转移方式是完全相同的,而且他们又独立所以支持乘法原理。所以考虑整体 ,转移时一次性考虑 个格子,我们需要现在需要做的是快速计算贡献。
开四个 bitset
维护每个序列的四种限制是否存在,那么只需要位运算就可以计算贡献。考虑如何处理出这些 bitset
,在暴力枚举 时,我们在线处理 表示起点集合为 的 bitset
;在 转移时,我们预处理 表示距离集合为 时的 bitset
,然后就可以得到转移系数。
时间复杂度
#include <cstdio>
#include <bitset>
#include <cstring>
#include <iostream>
using namespace std;
const int M = 1005;
const int N = 1<<16;
#define int long long
const int MOD = 1e9+7;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,k,a[N],b[N],p2[M],p3[M];
int ans,f[N][2],g[N][2],dp[N],lg[N];
bitset<M> R,s1,s2,s3,in[M];
struct node
{
bitset<M> b[4];
node operator | (const node &v) const
{
node r;
for(int i=0;i<4;i++) r.b[i]=b[i]|v.b[i];
return r;
}
void set(int x,int y) {b[x].set(y);}
int val()
{
s3=R&~((b[0]&b[1])|(b[2]&b[3]));
s1=s3&(b[0]^b[1]);s2=s3&(b[2]^b[3]);
int x=s1.count()+s2.count(),y=(s1&s2).count();
return p2[y]*p3[x-2*y]%MOD;
}
}h[M],t[N],Z;
void add(int &x,int y) {x=(x+y)%MOD;}
void work(int p)
{
R=in[p];int L=1<<(n-p+1);
memset(f,0,sizeof 0);f[0][0]=-1;
for(int i=0;i<L;i++)
{
a[i]=t[i].val();
node c9=t[i];c9.b[2].set();
b[i]=c9.val();//sad
}
for(int j=1;j<=n;j++)
{
for(int i=0;i<L;i++) g[i][0]=g[i][1]=0;
for(int i=0;i<L;i++)
{
int k=i<<1,w=k&(L-1),o=(k!=w),*z=(o?b:a);
if(j!=p)//not choose
{
if(j<p) add(g[w][o],f[i][0]*b[w]);
else add(g[w][o],f[i][0]*z[w]);
add(g[w][1],f[i][1]*b[w]);
}
if(j<=p)//choose
{
w|=1;
if(j<p) add(g[w][o],-f[i][0]*b[w]);
else add(g[w][o],-f[i][0]*z[w]);
add(g[w][1],-f[i][1]*b[w]);
}
}
swap(f,g);
}
for(int i=0;i<L;i++)
add(ans,f[i][0]+f[i][1]);
}
signed main()
{
n=read();m=read();k=n/2;
p2[0]=p3[0]=1;
for(int i=1;i<=m;i++)
{
static char s[M]={};
scanf("%s",s+1);Z.set(2,i);
int w=2,len=0,t=strlen(s+1);
p2[i]=p2[i-1]*2%MOD;
p3[i]=p3[i-1]*3%MOD;
for(int j=1;j<=t;j++)
{
if(s[j]=='R') h[len++].set(w,i),w=2;
else if(s[j]=='*') w^=1;
else w=s[j]-'0';
}
h[len].set(w,i);
for(int j=1;j<=n;j++)
if(len+j<=n) in[j].set(i);
for(int j=len+1;j<=n;j++) h[j].set(2,i);
}
dp[0]=lg[0]=-1;
for(int i=1;i<N;i++) lg[i]=lg[i>>1]+1;
for(int i=1;i<(1<<k);i++) dp[i]=-dp[i&(i-1)];
//p<=n/2
for(int i=1;i<=n;i++)
{
for(int s=1;s<(1<<k);s++)
{
int d=i-lg[s&(-s)]-1;
t[s]=t[s&(s-1)]|(d<0?Z:h[d]);
R=in[lg[s]+1];
dp[s]=dp[s]*t[s].val()%MOD;
}
}
for(int s=1;s<(1<<k);s++) add(ans,dp[s]);
//p>n/2
for(int s=1;s<N;s++)
t[s]=t[s&(s-1)]|h[lg[s&(-s)]];
for(int i=k+1;i<=n;i++) work(i);
printf("%lld\n",(ans+MOD)%MOD);
}
分类:
dp
, 其他-----时间复杂度
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
2021-07-27 CF1119F Niyaz and Small Degrees
2021-07-27 AtCoder Regular Contest 124
2021-07-27 CF827F Dirty Arkady's Kitchen