[NOI2021]机器人游戏

Posted StaroForgin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[NOI2021]机器人游戏相关的知识,希望对你有一定的参考价值。

机器人游戏

题解

听Cirno的蛊惑,来做了这道题,然后调了好久…

首先,我们根据样例解释,应该比较容易想到通过容斥来解决这个问题。
我们可以计算对于每个起点的组合,有多少个对于它合法的 X , Y X,Y X,Y组合。
显然,在有多个起点时,它会被几个不同的转移状况覆盖,那么我们就可以通过覆盖到它的转移状态。
总共的转移状态有四种, 0 0 0表示与之前相同, 1 1 1表示与之前不同, 2 2 2表示被赋值为 0 0 0 3 3 3表示赋值为 1 1 1
每种操作对于每个点的转移状态我们可以预处理出来。
显然,对于有至少一个起点的固定输入串,它的输出串是唯一的,毕竟每个字符的变化状态是固定的。
如果一个点作起点会爆掉,那这整个串就只能为空,特殊处理一下。
如果一个点同时被 0 0 0 1 1 1或者 2 2 2 3 3 3覆盖,那它就只有空格一种选择。
如果一个点同时被 0 0 0 1 1 1中的一个和 2 2 2 3 3 3中的一个覆盖,他就有 0 / 1 0/1 0/1中的某个数即空格两种选择。
否则,显然这个点是有 3 3 3种选择。
我们可以尝试对于一个起点组合,求出每个位置的倍覆盖情况。
暴力枚举起点集合,对于每个集合单独计算是 O ( 2 n n 2 m ) O\\left(2^nn^2m\\right) O(2nn2m)的。
如果我们通过 S S S S ⊕ l o w b i t ( x ) S\\oplus lowbit(x) Slowbit(x)转移过来,可以做到 O ( 2 n n m ) O\\left(2^nnm\\right) O(2nnm),大概有 20 p t s 20pts 20pts

考虑优化。
显然,对于 n ⩽ 32 n\\leqslant 32 n32这个数据范围,我们很容易想到折半来优化。
分析一下,如果我们现在对于一个没有爆掉的串,它有用的起点个数。
如果它走的步数大于 n 2 \\fracn2 2n,显然它有用的起点不会超过前面半,后面都会爆掉。
如果它走的步数小于 n 2 \\fracn2 2n,真正对我们当前讨论点有用的起点也就只有这个带点前面不超过 n 2 \\fracn2 2n个点,再前面就只有考虑有没有点被选即可,因为它们也只会让这个点拥有状态 0 0 0
由于涉及到有的起点可能会爆炸的问题,使得会导致有的行只能为空,对每个位置产生的贡献造成影响。
我们可以考虑枚举最后一个被选择的起点在哪里。
如果这个点在前半段,之间像我们上面的做法一样,枚举状态即可。
如果这个点在后半段,可以考虑状压 d p dp dp
我们就可以考虑记 d p i , S , 0 / 1 dp_i,S,0/1 dpi,S,0/1表示现在我们转移到了第 i i i个位置,前面不超过 n 2 \\fracn2 2n个点的状态为 S S S,在前面有没有选择点作为起点。
每次预处理一下,对于一个节点,它前面的起点选择状态为 S , 0 / 1 S,0/1 S,0/1时,它这一列所有纸条该位置的贡献乘积。
这样总共要预处理 n n n次,每次预处理时间复杂度 O ( 2 n 2 m ) O\\left(2^\\fracn2m\\right) O(22nm),预处理后进行一次 d p dp dp转移的复杂度为 2 n 2 n 2^\\fracn2n 22nn
这样的话可以做到 O ( 2 n 2 n ( m + n ) ) O\\left(2^\\fracn2n(m+n)\\right) O(22nn(m+n)),大概有 48 p t s 48pts 48pts

我们看看还有没有可以优化的地方。
我们预处理的时候那个 m m m非常显眼, m m m ⩽ 1000 \\leqslant 1000 1000的,就是它这里复杂度很大,后面 d p dp dp n n n就很小。
其实我们这里只涉及被某几个状态覆盖的该位置的纸条计数的计算,每个状态的表示事实上都是 b o o l bool bool类型,转移也就是 o r or or或者 a n d and and
这不是显然可以通过 bitset \\textbitset bitset进行优化吗?恰好我们的计数 bitset \\textbitset bitset也是可以轻松解决的。
我们记录 b i , 0 / 1 / 2 / 3 b_i,0/1/2/3 bi,0/1/2/3表示距离起点距离为 i i i,有哪些纸条被状态 0 / 1 / 2 / 3 0/1/2/3 0/1/2/3覆盖。
之后的转移与求解比较容易,直接操作即可。
恰好我们前面那个枚举的部分也是能用类似的方法优化的。
于是我们轻松的把这部分优化到了 O ( 2 n 2 m ω ) O\\left(\\frac2^\\fracn2m\\omega\\right) O(ω22nm)

总时间复杂度 O ( 2 n 2 ( n + m ω ) n ) O\\left(2^\\fracn2(n+\\fracm\\omega)n\\right) O(22n(n+ωm)n)
然而 O n e I n D a r k \\rm O\\redneInDark OneInDark O ( 2 n 2 n + n m ) O\\left(2^\\fracn2n+nm\\right) O(22nn+nm)的做法,快点膜它。

源码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
#define MAXN 1005
#define MAXM (1<<16)+5
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lowbit(x) (x&-x)
const int mo=1e9+7;
const int jzm=23;
const int zero=200000;
template<typename _T>
_T Fabs(_T x)return x<0?-x:x;
template<typename _T>
void read(_T &x)
    _T f=1;x=0;char s=getchar();
    while(s<'0'||s>'9')if(s=='-')f=-1;s=getchar();
    while('0'<=s&&s<='9')x=(x<<3)+(x<<1)+(s^48);s=getchar();
    x*=f;

int add(int x,int y,int p)return x+y<p?x+y:x+y-p;
void Add(int &x,int y,int p)x=add(x,y,p);
int qkpow(int a,int s,int p)int t=1;while(s)if(s&1)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1;return t;
int n,m,len[MAXN],low[MAXM],hig[MAXM],bit[MAXM],pw2[MAXN],pw3[MAXN];
int dp[2][MAXM][2],g[MAXM][2],ans;char str[105];
bitset<MAXN>b[35以上是关于[NOI2021]机器人游戏的主要内容,如果未能解决你的问题,请参考以下文章

icodelab 另一个转圈游戏

NOI2017 [NOI2017]游戏 2-sat

BZOJ 4199[Noi2015]品酒大会 后缀自动机+DP

8782:乘积最大

搜索7--noi1804:小游戏

[NOI1997] 积木游戏(dp)