\(\cal{T_1}\text{ Board Game}\)
题目描述
\(\text{Alice}\) 和 \(\text{Bob}\) 玩棋类游戏。他们在一个 \(n\times m\) 的空棋盘上轮流下棋,\(\text{Alice}\) 先下。和一般的棋类游戏不同的是,\(\text{Alice}\) 和 \(\text{Bob}\) 用同样颜色的棋子。他们在下棋之前,共同决定了一些图案。如果有人下完一步后,棋盘上出现了任意一个之前决定的图案,那么这个人就赢了。现在给出他们下棋前决定的图案,假设两人都按照最优方法下棋,你需要判断这个游戏是 \(\text{Alice}\) 获胜还是 \(\text{Bob}\) 获胜。
\(nm\le 27\).
解法
首先有一个 \(\mathcal O(nm\cdot 2^{nm})\) 的高维前缀和的做法,先处理出 \(ed_s\) 表示状态 \(s\) 是否为终止态,再用 \(dp_s\) 表示从状态 \(s\) 开始为必胜/败,最后判断 \(dp_0\) 即可。
考虑优化。考虑 \(ed_s,dp_s\) 都存储布尔变量,我们可以将状态的后六位的值压成一个 unsigned long long
变量,比如 \(ed_{(1000100)_2}=1\),压缩后相当于 \(ed_{(1)_2}\) 的第 \((000100)_2\) 位为 \(1\).
对于 \(ed_s\),先处理后六位的转移,再转移之前的部分。预处理 \(fr_i\) 表示 \(fr_i\) 表示的集合可以利用第 \(i\) 位向外转移,枚举 \(i\in[0,6)\),将 \(ed_s\) 并上 \(fr_i\) 表示可以转移并且有用的状态,转移就是右移 \(2^i\) 位,对应二进制 "加上 \(2^i\)"。这部分的转移是 \(\mathcal O((nm-6)\cdot 2^{nm-6})\).
接下来只用考虑 \(\mathtt{dp}\) 数组的转移,由于我们要逆推,所以先转移之前的部分,再处理后六位的转移。对于后六位的转移,预处理 \(to_s\) 表示 \(s\) 可以拓展到哪些集合,枚举 \(i\in[0,2^6)\) 转移即可。需要注意的是要特别判断状态 \(s\) 是否为终止态。复杂度为 \(\mathcal O(2^{nm})\). 最后如果 \(dp_0\) 为奇数就是 \(\rm Alice\) 胜利,这是因为 \(dp_0\) 的 \(0\) 号位表示的才是 \(0\) 状态。
代码
#include <cstdio>
#define print(x,y) write(x),putchar(y)
template <class T>
inline T read(const T sample) {
T x=0; char s; bool f=0;
while((s=getchar())>'9' || s<'0')
f |= (s=='-');
while(s>='0' && s<='9')
x = (x<<1)+(x<<3)+(s^48),
s = getchar();
return f?-x:x;
}
template <class T>
inline void write(T x) {
static int writ[40],w_tp=0;
if(x<0) putchar('-'),x=-x;
do writ[++w_tp]=x-x/10*10,x/=10; while(x);
while(w_tp) putchar(writ[w_tp--]^48);
}
typedef unsigned long long ull;
char g[30][30];
int n,m,lim,h,w,k;
ull ed[1<<21],dp[1<<21],fr[6],to[64];
int getVal(int s,int t) {
int ret=0;
for(int i=0;i<h;++i)
for(int j=0;j<w;++j)
ret |= ((g[i][j]=='1')<<((s+i)*m+t+j));
return ret;
}
int main() {
n=read(9), m=read(9), k=n*m-6; int val;
for(int T=read(9); T; --T) {
h=read(9), w=read(9);
for(int i=0;i<h;++i) scanf("%s",g[i]);
for(int i=0;i<=n-h;++i)
for(int j=0;j<=m-w;++j)
val = getVal(i,j),
ed[val>>6] |= (1ull<<(val&63));
}
lim = (1<<k);
for(int s=0;s<64;++s)
for(int i=0;i<6;++i)
if(!(s>>i&1)) {
fr[i] |= (1ull<<s);
to[s] |= (1ull<<(s|(1<<i)));
}
for(int s=0;s<lim;++s) {
for(int i=0;i<6;++i)
ed[s] |= ((ed[s]&fr[i])<<(1<<i));
for(int i=0;i<k;++i) if(!(s>>i&1))
ed[s|(1<<i)] |= ed[s];
}
for(int s=lim-1;s>=0;--s) {
for(int i=0;i<k;++i) if(!(s>>i&1))
dp[s] |= (~dp[s|(1<<i)]);
dp[s] &= (~ed[s]);
for(int i=63;i>=0;--i) if(!(ed[s]>>i&1)) {
if((~dp[s])&to[i]) dp[s] |= (1ull<<i);
dp[s] &= (~ed[s]);
}
}
puts((dp[0]&1)?"Alice":"Bob");
return 0;
}
\(\cal{T_2}\text{ Function Query}\)
咕咕咕……
\(\cal{T_3}\text{ Minimal DFA}\)
咕咕咕……