Wannafly挑战赛15
有向无环图直接求每个点的sg,放棋子相当于是一个新点。每个棋子对应一个游戏,最后求nim游戏和。那么问题在于求
,用莫队+分块直接就能做。
然后学习一波主席树的做法。对于
,相当于在前缀
,由sg值构成权值线段树,每个点存其出现的最大位置,那么二分出现位置小于L的最小sg值。
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define ALL(X) (X).begin(), (X).end()
#define CLR(A, X) memset(A, X, sizeof(A))
#define bitcount(X) __builtin_popcountll(X)
typedef double DB;
typedef long long LL;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f;
const int N = 1e5+10;
int cnt[N], sg[N];
vector<int> G[N];
struct Node { int l, r, d; } T[N*20];
int root[N], qv, qd, node_cnt;
void build(int &o, int L, int R, int pre) {
o = ++node_cnt;
T[o] = T[pre];
if(L == R) {
T[o].d = qd;
return;
}
int M = (L+R)>>1;
if(qv <= M) build(T[o].l, L, M, T[pre].l);
else build(T[o].r, M+1, R, T[pre].r);
T[o].d = min(T[T[o].l].d, T[T[o].r].d);
}
int query(int o, int L, int R, int k) {
if(L == R) return L;
int M = (L+R)>>1;
if(T[T[o].l].d < k) return query(T[o].l, L, M, k);
return query(T[o].r, M+1, R, k);
}
int main() {
int n, m, q;
scanf("%d%d%d", &n, &m, &q);
while(m--) {
int u, v;
scanf("%d%d", &u, &v);
G[u].pb(v);
}
int mx = -1;
for(int i = 1; i <= n; i++) {
sg[i] = mx+1;
for(int v:G[i]) if(--cnt[sg[v]] == 0) {
sg[i] = min(sg[i], sg[v]);
}
for(int v:G[i]) cnt[sg[v]]++;
cnt[sg[i]]++;
mx = max(mx, sg[i]);
qv = sg[i], qd = i;
build(root[i], 0, n, root[i-1]);
}
int ans = 0;
while(q--) {
int l, r;
scanf("%d%d", &l, &r);
ans ^= query(root[r], 0, n, l);
}
printf("%s\n", ans?"Alice":"Bob");
return 0;
}