原创题又没有链接
题解
题解
这题可以先把棋盘的状态拍平成01序列,然后每个图形处理一下任意平移过后的状态,那么这些状态以及他们的超集都是必败态。
我们可以做一个朴素的 O ( n m 2 n m ) O(nm2^{nm}) O(nm2nm) 的沃尔什变换来预处理得到每个状态是否必定是必败态,然后再 O ( n m 2 n m ) O(nm2^{nm}) O(nm2nm) 做一下状压DP,用定义确定每个状态的胜负,这样可以得 50 分。
这个时候一定要想起神的教导:
如果DP状态里存的是bool的话,考虑用int存来优化状态。——JZM
我们发现不管是我们一开始的预处理还是后面的DP,每个位置存的都是 bool,非常浪费时间空间,所以我们考虑压位,把每64个bool分块,压缩到一个unsigned long long
里面,这样数组只用开到
2
n
m
−
6
2^{nm-6}
2nm−6 的大小(结合数据范围的提示:
n
×
m
≥
6
n\times m\ge 6
n×m≥6 😀)。
考虑第一步沃尔什变换,由于我们不用考虑算重,所以就用普通的从小到大枚举状态+枚举转移位的DP代替。我们先考虑跨块的转移,这个可以用按位或解决;块内转移可以枚举转移距离 2 i ( 0 ≤ i < 6 ) 2^i(0\le i<6) 2i(0≤i<6),对每个 i i i 预处理出哪些位置可以往后转移,然后就可以按位与+位移+按位或直接解决。这部分可以做到 O ( n m 2 n m − 6 ) O(nm2^{nm-6}) O(nm2nm−6)。
考虑第二步DP,我们还是先处理跨块的转移,相当于要先取反再按位或。块内转移可以直接枚举 64 个位置,( 此处要预处理每个位置可以从块内哪些位置转移过来),先取反,然后用按位与提取出来,判断是否含有1即可。由于操作较复杂,要先取反再或起来,所以这部分没法做到前面的优秀复杂度,只能做到 O ( 2 n m ) O(2^{nm}) O(2nm)。
代码
卡常技巧:第二步DP的时候利用第一步预处理的信息剪枝,然后开 Ofast
#include<bits/stdc++.h>//JZM yyds!!
#define ll long long
#define lll __int128
#define uns unsigned
#define IF (it->first)
#define IS (it->second)
#define END putchar('\n')
#pragma GCC optimize("Ofast")
using namespace std;
const int MAXN=-1;
const ll INF=1e18;
inline ll read(){
ll x=0;bool f=1;char s=getchar();
while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
return f?x:-x;
}
int ptf[50],lpt;
inline void print(ll x,char c='\n'){
if(x<0)putchar('-'),x=-x;
ptf[lpt=1]=x%10;
while(x>9)x/=10,ptf[++lpt]=x%10;
while(lpt)putchar(ptf[lpt--]^48);
if(c>0)putchar(c);
}
inline ll lowbit(ll x){return x&-x;}
uns ll vis[1<<21],a[6],b[6],dp[1<<21],c[100];
bool as[30][30];
int n,m,k;
char in[30];
inline ll geths(int x,int y,int h,int w){
ll res=0;
for(int i=0;i<h;i++)
for(int j=0;j<w;j++)
if(as[i][j])res|=(1ll<<((x+i)*m+y+j));
return res;
}
signed main()
{
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
n=read(),m=read(),k=n*m-6;
for(int K=read();K--;){
int h=read(),w=read();
for(int i=0;i<h;i++){
scanf("%s",in);
for(int j=0;j<w;j++)as[i][j]=(in[j]=='1');
}
for(int i=0;i<=n-h;i++)
for(int j=0;j<=m-w;j++){
ll hs=geths(i,j,h,w);
vis[hs>>6]|=(1ull<<(hs&63));
}
}
for(int i=0;i<6;i++)
for(int s=0;s<64;s++){
if((s>>i)&1)b[i]|=(1ull<<s);
else a[i]|=(1ull<<s);
}
for(int s=0;s<64;s++)
for(int i=0;i<6;i++)if(~(s>>i)&1)c[s]|=(1ull<<(s|(1<<i)));
for(int s=0;s<(1<<k);s++){
for(int i=0;i<6;i++)
vis[s]|=((vis[s]&a[i])<<(1<<i));
for(int i=0;i<k;i++)if(~(s>>i)&1)
vis[s|(1<<i)]|=vis[s];
}
for(int s=(1<<k)-1;s>=0;s--){
dp[s]=0;
for(int i=0;i<k;i++)
if(~(s>>i)&1)dp[s]|=(~dp[s|(1<<i)]);
dp[s]&=(~vis[s]);
for(int i=63;i>=0;i--)if(~(vis[s]>>i)&1){
uns ll f=(~dp[s])&c[i];
if(f)dp[s]|=(1ull<<i);
dp[s]&=(~vis[s]);
}
}
printf((dp[0]&1)?"Alice\n":"Bob\n");
return 0;
}