题解 Board Game
我脑残了判存在只会暴力
其实压位之后可以判子集
那么就有一个 \(O(nm2^{nm})\) 的暴力了:
令 \(f_s\) 为状态为 \(s\) 是否包含了给定图案
令 \(dp_s\) 为一个人放棋子后状态为 \(s\) 这个人是否必胜
转移就按定义转移即可,可以有 50 pts
然后注意转移的时候枚举 lowbit 就可以有 75 pts
然后指令集优化一下就可以有 100 pts
那么这个题就讲完了
好的那么考虑优化
发现 \(n\times m=27\)
- 只有高维前缀和的可行性 DP 可以压位从 \(O(n2^n)\) 优化到 \(O(2^n)\)
具体地,将最低 \(6\) 位压进一个 ull 里
一共 \(2^6=64\) 个状态对应这个 ull 的 64 个位
那么转移时高 \(n-6\) 位暴力转移
低 \(6\) 位可以预处理一些东西来优化
若是 \(s\to s|(1\ll i)\),那么可以预处理 \(sub_s\),其中若 \(t\) 是 \(s\) 的恰好少一个 1 的子集则 \(sub_s\) 的第 \(t\) 位为 1
那么从低到高转移,某一个位置 \(s\) 值为 1 的条件就是 \(f\&sub_s\) 不为 0
这样的复杂度是 \(O(2^{n-6}\times 2^6)=O(2^n)\) 的
对于这道题,dp 数组的转移可以从高到低考虑
那么 dp[s]&=~dp[s|(1<<i)]
注意要先处理前 \(n-6\) 位的转移再考虑后 6 位的转移,否则转移后 6 位的时候无法保证使用的状态是合法的
举个例子:
\(f_{000001}\) 应为 0,但在未转移高位前为 1
那么 \(f_{000000}\) 这样转移就会出问题
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define fir first
#define sec second
#define ll long long
#define ull unsigned long long
//#define int long long
int n, m, k, h, w;
char s[N];
namespace force{
short any_mp[1<<12], rec[1<<12];
int a[12][12], tot;
// unordered_map<ull, bool> mp;
pair<pair<int, int>, int> ban[N];
int ban2[12][12][1050], cnt[12][12];
const ull base1=13131, base2=131;
// ull hashing(int u, int v, int n, int m, int a[30][30]) {
// ull ans=0;
// for (int i=u; i<=n; ++i) {
// ull t=0;
// for (int j=v; j<=m; ++j) t=t*base1+a[i][j];
// ans=ans*base2+t;
// }
// return ans;
// }
// bool any(int s) {
// int a[30][30];
// decode(s, a);
// for (int i=1; i<=n; ++i)
// for (int j=1; j<=m; ++j)
// for (int k=i; k<=n; ++k)
// for (int l=j; j<=m; ++j)
// if (mp.find(hashing(i, j, k, l, a))!=mp.end())
// return 1;
// return 0;
// }
pair<pair<int, int>, int> encode(int n, int m, int a[12][12]) {
int ans=0, tot=0;
for (int i=1; i<=n; ++i)
for (int j=1; j<=m; ++j,++tot)
if (a[i][j]) ans|=1<<tot;
return {{n, m}, ans};
}
int encode(int a[12][12]) {
int ans=0, tot=0;
for (int i=1; i<=n; ++i)
for (int j=1; j<=m; ++j,++tot)
if (a[i][j]) ans|=1<<tot;
return ans;
}
void decode(int s, int a[12][12]) {
int tot=0;
for (int i=1; i<=n; ++i)
for (int j=1; j<=m; ++j,++tot)
if (s&(1<<tot)) a[i][j]=1;
else a[i][j]=0;
}
bool any(int s) {
if (any_mp[s]!=-1) return any_mp[s];
int a[12][12];
decode(s, a);
for (int i=1; i<=n; ++i)
for (int j=1; j<=m; ++j)
for (int k=i; k<=n; ++k)
for (int l=j; l<=m; ++l) {
int u=n-i+1, v=m-j+1, t=encode(u, v, a).sec;
for (int now=1; now<=cnt[u][v]; ++now)
if ((ban2[u][v][now]&t)==ban2[u][v][now])
return any_mp[s]=1;
}
return any_mp[s]=0;
}
bool dfs(int s) { // return true if the first one always wins.
int a[12][12];
if (rec[s]!=-1) return rec[s];
decode(s, a);
// cout<<"---a---"<<endl;
// for (int i=1; i<=n; ++i) {for (int j=1; j<=m; ++j) cout<<a[i][j]<<' '; cout<<endl;}
bool any_lose=0;
for (int i=1; i<=n; ++i)
for (int j=1; j<=m; ++j) if (!a[i][j]) {
a[i][j]=1;
int s=encode(a);
if (any(s)) return rec[s]=1;
if (!dfs(s)) {any_lose=1; goto jump;}
a[i][j]=0;
}
jump: ;
return rec[s]=any_lose;
}
void solve() {
// cout<<double(sizeof(ban2)+sizeof(any_mp)+sizeof(ban)+sizeof(ban2))/1000/1000<<endl;
memset(any_mp, -1, sizeof(any_mp));
memset(rec, -1, sizeof(rec));
for (int i=1; i<=k; ++i) {
scanf("%d%d", &h, &w);
for (int j=1; j<=h; ++j) {
scanf("%s", s+1);
for (int k=1; k<=w; ++k) a[j][k]=(s[k]=='1');
}
ban[++tot]=encode(h, w, a);
}
sort(ban+1, ban+tot+1);
tot=unique(ban+1, ban+tot+1)-ban-1;
for (int i=1; i<=tot; ++i) {
int u=ban[i].fir.fir, v=ban[i].fir.sec;
ban2[u][v][++cnt[u][v]]=ban[i].sec;
}
puts(dfs(0)?"Alice":"Bob");
}
}
namespace task1{
int f[30][30];
bool dfs(int a, int b) {
if (a<0||b<0) return 1;
if (f[a][b]!=-1) return f[a][b];
if (a<=1) return f[a][b]=1;
else return f[a][b]=(dfs(a-1, b)&dfs(a, b-1))?0:1;
}
void solve() {
memset(f, -1, sizeof(f));
int cnt0=0, cnt1=0;
scanf("%d%d", &h, &w);
for (int i=1; i<=h; ++i) {
scanf("%s", s+1);
for (int j=1; j<=w; ++j)
if (s[j]=='1') ++cnt1;
else ++cnt0;
}
puts(dfs(cnt1, cnt0)?"Alice":"Bob");
}
}
namespace task2{
int a[30][30];
bool f[1<<24], dp[1<<24];
int encode(int a[30][30]) {
int ans=0, tot=0;
for (int i=1; i<=n; ++i)
for (int j=1; j<=m; ++j,++tot)
if (a[i][j]) ans|=1<<tot;
return ans;
}
void solve() {
for (int i=1; i<=k; ++i) {
memset(a, 0, sizeof(a));
scanf("%d%d", &h, &w);
for (int j=1; j<=h; ++j) {
scanf("%s", s+1);
for (int k=1; k<=w; ++k) a[j][k]=(s[k]=='1');
}
int s=encode(a);
// cout<<"s: "<<bitset<20>(s)<<endl;
for (int j=h; j<=n; ++j)
for (int k=w; k<=m; ++k)
f[s<<(k-w)<<(m*(j-h))]=1;
}
int lim=1<<(n*m);
for (int s=0; s<lim; ++s) dp[s]=1;
for (int s=0; s<lim; ++s) if (f[s])
for (int i=0; i<(n*m); ++i) if (!(s&(1<<i)))
f[s|(1<<i)]|=f[s];
for (int s=lim-1; ~s; --s) if (dp[s]|f[s]) {
dp[s]|=f[s];
for (int i=0; i<(n*m); ++i) if (s&(1<<i))
dp[s^(1<<i)]=0;
}
puts(dp[0]?"Bob":"Alice");
}
}
namespace task{
int a[30][30];
ull f[1<<23], dp[1<<23], vis[1<<23], sub[1<<6], sup[1<<6];
bool f2[1<<23], dp2[1<<23];
int encode(int a[30][30]) {
int ans=0, tot=0;
for (int i=1; i<=n; ++i)
for (int j=1; j<=m; ++j,++tot)
if (a[i][j]) ans|=1<<tot;
return ans;
}
void solve() {
for (int i=1; i<=k; ++i) {
memset(a, 0, sizeof(a));
scanf("%d%d", &h, &w);
for (int j=1; j<=h; ++j) {
scanf("%s", s+1);
for (int k=1; k<=w; ++k) a[j][k]=(s[k]=='1');
}
int s=encode(a);
// cout<<"s: "<<bitset<20>(s)<<endl;
for (int j=h; j<=n; ++j)
for (int k=w; k<=m; ++k) {
int t=s<<(k-w)<<(m*(j-h));
f[t>>6]|=1llu<<(t&63);
}
for (int j=h; j<=n; ++j)
for (int k=w; k<=m; ++k)
f2[s<<(k-w)<<(m*(j-h))]=1;
}
int len=max(n*m-6, 0), lim=1<<len, lim2=1<<6;
for (int s=0; s<lim2; ++s)
for (int i=0; i<6; ++i) if (!(s&(1<<i)))
sub[s|(1<<i)]|=1llu<<s, sup[s]|=1llu<<(s|(1<<i));
for (int s=0; s<lim; ++s) {
for (int i=0; i<lim2; ++i)
if (f[s]&sub[i]) f[s]|=1llu<<i;
// for (int i=0; i<len; ++i) if (!(s&(1<<i)))
// f[s|(1<<i)]|=f[s];
for (int t=(~s)&(lim-1); t; t-=t&-t)
f[s|(t&-t)]|=f[s];
}
for (int s=lim-1; ~s; --s) {
dp[s]=~dp[s];
// for (int i=0; i<len; ++i) if (!(s&(1<<i)))
// dp[s]&=~dp[s|(1<<i)];
for (int t=(~s)&(lim-1); t; t-=t&-t)
dp[s]&=~dp[s|(t&-t)];
dp[s]|=f[s];
for (int i=lim2-1; ~i; --i)
if (((~dp[s])&sup[i])!=sup[i])
dp[s]&=~(1llu<<i), dp[s]|=f[s];
// if (!s && i==34)
// if (!s) cout<<(dp[s]&(1ll<<34)?1:0)<<' '; cout<<endl;
// cout<<bitset<5>(dp[s])<<endl;
// cout<<bitset<5>(sup[1])<<endl;
// if (!s) cout<<(dp[s]&(1ll<<0)?1:0)<<endl;
// for (int i=(1<<6)-1; ~i; --i) {
// cout<<"dp: "<<bitset<7>((s<<1)|i)<<' '<<(dp[s]&(1ll<<i)?1:0)<<endl;
// }
}
// int lim2=1<<(n*m);
// for (int s=0; s<lim2; ++s) dp2[s]=1;
// for (int s=0; s<lim2; ++s) if (f2[s])
// for (int i=0; i<(n*m); ++i) if (!(s&(1<<i)))
// f2[s|(1<<i)]|=f2[s];
// for (int s=0; s<lim2; ++s)
// if (f2[s]) assert(f[s>>6]&(1llu<<(s&63)));
// else assert(!(f[s>>6]&(1llu<<(s&63))));
// for (int s=lim2-1; ~s; --s) if (dp2[s]|f2[s]) {
// dp2[s]|=f2[s];
// for (int i=0; i<(n*m); ++i) if (s&(1<<i))
// dp2[s^(1<<i)]=0;
// }
// // for (int s=lim2-1; ~s; --s) cout<<"dp2: "<<bitset<7>(s)<<' '<<dp2[s]<<endl;
// for (int s=0; s<lim2; ++s)
// if (dp2[s]) assert(dp[s>>6]&(1llu<<(s&63)));
// else assert(!(dp[s>>6]&(1llu<<(s&63))));
puts((dp[0]&1)?"Bob":"Alice");
}
}
signed main()
{
freopen("game.in", "r", stdin);
freopen("game.out", "w", stdout);
scanf("%d%d%d", &n, &m, &k);
// force::solve();
// task1::solve();
// if (n*m>10) task1::solve();
// else force::solve();
// task2::solve();
task::solve();
return 0;
}