\(\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}\)

咕咕咕……

posted on 2020-04-03 16:11  Oxide  阅读(106)  评论(0编辑  收藏  举报