给你一个密码锁(至多五位),告诉你一开始的样子,然后两个人轮流选一个位置往上或者往下拧一格。
然后要使得每次得到的数字之前没有出现过且不是给出的数字。
要你判断是否先手必胜。
麻了写了一天。
这个是二分图博弈的可以说是模板把,毕竟这个密码锁每次操作必定奇偶变化应该能看出来(我除外)。
然后就是二分图博弈的样子:
一个二分图,然后从一个点开始每个人轮流把它按边移动,每次要移动到一个新的位置,无法移动着失败。
然后先是结论:如果这个图所有最大匹配的方案都包含了起点,那先手必胜,否则后手必胜。
然后是证明:
充分性:如果包含起点
S
S
S,那先手每一步如果跟着匹配边,那后手只能跟着走。因为如果不跟着走,那形成一套
S
→
p
1
→
p
2
→
p
n
S\rightarrow p_1\rightarrow p_2\rightarrow p_n
S→p1→p2→pn,那匹配边就是
S
−
p
1
,
p
2
−
p
3
,
.
.
.
,
p
n
−
2
−
p
n
−
1
S-p_1,p_2-p_3,...,p_{n-2}-p_{n-1}
S−p1,p2−p3,...,pn−2−pn−1。
那我们完全可以集体右移:
p
1
−
p
2
,
p
3
−
p
4
,
.
.
.
,
p
n
−
1
−
p
n
p_1-p_2,p_3-p_4,...,p_{n-1}-p_{n}
p1−p2,p3−p4,...,pn−1−pn,这样长度相同也是最大匹配,但是却不包含了
S
S
S。
必要性:如果不包含起点
S
S
S,那对于一个不包含
S
S
S 的最大匹配。第一步肯定是走在匹配点上(不然这条边是可以放进最大匹配里面的),而后手沿着匹配边一直走的话,先手是走不出的。
因为如果最后一个是非匹配点
S
→
p
1
→
p
2
→
p
n
S\rightarrow p_1\rightarrow p_2\rightarrow p_n
S→p1→p2→pn,匹配是
p
1
−
p
2
,
.
.
.
p
n
−
2
−
p
n
−
1
p_1-p_2,...p_{n-2}-p_{n-1}
p1−p2,...pn−2−pn−1,你完全可以左右各扩展一下得到
S
−
p
1
,
p
2
−
p
3
,
.
.
.
,
p
n
−
1
−
p
n
S-p_1,p_2-p_3,...,p_{n-1}-p_n
S−p1,p2−p3,...,pn−1−pn,那就不是最大匹配了。
然后接着考虑如何判断一个点是否一定在最大匹配中。
其实有一个最直观的办法(也就是我这里用的),就是去掉这个点跑一遍,再加上这个点跑一遍,看两次的最大匹配是否相同。(这里你可以不给之前的边流量重新复制,那你就只需要判第二次是否有流量即可)
然后还有一种方法就是你可以先跑出一个最大匹配,然后看看是否存在取代边(就是一条匹配边的一个点和一个非匹配的点有连边,那另一个点可以被取代)。
然后新被取代的又可以去取代别人,然后每个没有被匹配的都这么试一下,就可以得到那些点可以被取代哪些不可以里。
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-fwhole-program")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-fstrict-overflow")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-skip-blocks")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fhoist-adjacent-loads")
#pragma GCC optimize("-frerun-cse-after-loop")
#pragma GCC optimize("inline-small-functions")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fexpensive-optimizations")
#pragma GCC optimize("-funsafe-loop-optimizations")
#pragma GCC optimize("inline-functions-called-once")
#pragma GCC optimize("-fdelete-null-pointer-checks")
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF 0x3f3f3f3f
using namespace std;
const int N = 1e5 + 5;
int m, n, ST, M, a[] = {1, 10, 100, 1000, 10000, 100000}, le[N], KK;
int tot, S, T, deg[N], lee[N];
bool sp[N], odd[N];
queue <int> q;
struct node {
int x, to, nxt, op;
}e[N * 20];
int read() {
int re = 0; char c = getchar();
while (c < '0' || c > '9') c = getchar();
while (c >= '0' && c <= '9') {
re = (re << 3) + (re << 1) + c - '0';
c = getchar();
}
return re;
}
void Init() {
KK = 0; for (int i = 0; i < N; i++) le[i] = -1;
memset(sp, 0, sizeof(sp));
}
void link(int x, int y, int z) {
e[++KK] = (node){z, y, le[x], KK + 1}; le[x] = KK; e[++KK] = (node){0, x, le[y], KK - 1}; le[y] = KK;
}
void calc(int i) {
for(int j = 0; j < m; j++) {
int bit = i / a[j] % 10, x;
if(bit != 9) x = i + a[j];
else x = i - 9 * a[j];
if (!sp[x]) link(i, x, 1);
if (bit) x = i - a[j];
else x = i + 9 * a[j];
if (!sp[x]) link(i, x, 1);
}
}
bool bfs() {
for (int i = 0; i <= tot; i++) lee[i] = le[i], deg[i] = 0;
q.push(S); deg[S] = 1;
while (!q.empty()) {
int now = q.front(); q.pop();
for (int i = le[now]; ~i; i = e[i].nxt)
if (!deg[e[i].to] && e[i].x) {
deg[e[i].to] = deg[now] + 1;
q.push(e[i].to);
}
}
return deg[T];
}
int dfs(int now, int sum) {
if (now == T) return sum;
int go = 0;
for (int &i = lee[now]; ~i; i = e[i].nxt)
if (deg[e[i].to] == deg[now] + 1 && e[i].x) {
int this_go = dfs(e[i].to, min(e[i].x, sum - go));
if (this_go) {
e[i].x -= this_go; e[e[i].op].x += this_go;
go += this_go; if (go == sum) return go;
}
}
if (go == 0) deg[now] = 0;
return go;
}
int dinic() {
int re = 0; while (bfs()) re += dfs(S, INF); return re;
}
int main() {
for (int i = 0; i < N; i++) {
int id = 0, x = i; while (x) {id += x % 10; x /= 10;} odd[i] = id & 1;
}
int TT = read();
while (TT--) {
Init();
m = read(); n = read(); ST = read();
M = a[m]; tot = M; S = ++tot; T = ++tot;
for (int i = 1; i <= n; i++) {
int x = read(); sp[x] = 1;
}
for(int i = 0; i < M; i++) {
if(sp[i]) continue;
if(odd[i] == odd[ST]) {
calc(i);
if (i != ST) link(S, i, 1);
}
else {
link(i, T, 1);
}
}
dinic();
link(S, ST, 1);
if (dinic()) printf("Alice\n");
else printf("Bob\n");
}
return 0;
}