[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)
S⊕lowbit(x)转移过来,可以做到
O
(
2
n
n
m
)
O\\left(2^nnm\\right)
O(2nnm),大概有
20
p
t
s
20pts
20pts。
考虑优化。
显然,对于
n
⩽
32
n\\leqslant 32
n⩽32这个数据范围,我们很容易想到折半来优化。
分析一下,如果我们现在对于一个没有爆掉的串,它有用的起点个数。
如果它走的步数大于
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]机器人游戏的主要内容,如果未能解决你的问题,请参考以下文章