[hdu 5963 朋友]
中文题目,直接copy题面了。
Problem Description
B君在围观一群男生和一群女生玩游戏,具体来说游戏是这样的:
给出一棵n个节点的树,这棵树的每条边有一个权值,这个权值只可能是0或1。 在一局游戏开始时,会确定一个节点作为根。接下来从女生开始,双方轮流进行 操作。
当一方操作时,他们需要先选择一个不为根的点,满足该点到其父亲的边权为1; 然后找出这个点到根节点的简单路径,将路径上所有边的权值翻转(即0变成1,1 变成0 )。
当一方无法操作时(即所有边的边权均为0),另一方就获得了胜利。
如果在双方均采用最优策略的情况下,女生会获胜,则输出“Girls win!”,否则输 出“Boys win!”。
为了让游戏更有趣味性,在每局之间可能会有修改边权的操作,而且每局游戏指 定的根节点也可能是不同的。
具体来说,修改边权和进行游戏的操作一共有m个,具体如下:
Input
包含至多5组测试数据。
第一行有一个正整数,表示数据的组数。
接下来每组数据第一行,有二个空格隔开的正整数 n,m ,分别表示点的个数,操 作个数。保证 n,m<40000 。
接下来n-1行,每行三个整数x,y,z,表示树的一条边。保证 1<x<n,1<y<n,0≤z≤1 。
接下来m行,每行一个操作,含义如前所述。保证一定只会出现前文中提到的两 种格式。
对于操作0,保证 1<=x<=n ;对于操作1,保证 1≤x≤n,1≤y≤n,0≤z≤1 ,保证树上存在一条边连接x和y。
Output
对于每组数据的每一个询问操作,输出一行“Boys win!”或者“Girls win!”。
Sample Input
2
2 3
1 2 0
0 1
1 2 1 1
0 2
4 11
1 2 1
2 3 1
3 4 0
0 1
0 2
0 3
0 4
1 2 1 0
0 1
0 2
0 3
1 3 4 1
0 3
0 4
Sample Output
Boys win!
Girls win!
Girls win!
Boys win!
Girls win!
Boys win!
Boys win!
Girls win!
Girls win!
Boys win!
Girls win!
首先呢。对于操作 0 x ,显然是一个博弈问题。可以先在一条树链上面(即先考虑顶点 x 只有一条边与其相连的情况),按照SG函数,手算一下, 找出结论: 在一条树链上面,先手赢当且仅当与 x 相连的边权为 1 。这个很重要的。
然后,很容易推算,对于多条树链的情况,很容易得出结论:先手赢当且仅当与 x 相连的所有边权异或和为 1 。
至此,题目已经解决了一大部分。
然后,我们的任务就是统计顶点 x 所有相连边的异或和,还有更新顶点 u 到顶点 v 的路径上的边权。
对于操作二(更新边权)。我们容易想到的做法就是,树链剖分。
所以问题又转化为一个区间覆盖更新,然后单点查询的一个边权的树链剖分。
对于操作一, 0 x ,就是一个遍历所有跟顶点 x 相连的边,单点查询边权,然后求异或和的过程。【但是,请注意,虽然做法是这么写的, 但是我觉得按道理这个做法应该是TLE的。因为每次询问的复杂度是 O(E∗log(E)) 】。
#include
using namespace std;
#define FIN freopen("input.txt", "r", stdin)
#define lson l, mid, (rt << 1)
#define rson mid + 1, r, (rt << 1 | 1)
#define __mid__ int mid = (l + r) >> 1
typedef long long LL;
const int MAXN = 40000 + 5;
int T, N, M;
struct Edge {
int v, next;
Edge() {}
Edge(int v, int next) : v(v), next(next) {}
} edge[MAXN << 1];
int head[MAXN], ESZ, E[MAXN][3];
int siz[MAXN], top[MAXN], fa[MAXN], son[MAXN], dep[MAXN], tid[MAXN], rk[MAXN], id;
int seg[MAXN * 3];
void init() {
ESZ = 0;
id = 0;
memset(head, -1, sizeof(head));
memset(son, -1, sizeof(son));
}
void add_edge(int u, int v) {
edge[ESZ] = Edge(v, head[u]);
head[u] = ESZ ++;
}
void dfs1(int u, int pre, int k) {
int v;
siz[u] = 1;
fa[u] = pre;
dep[u] = k;
for(int i = head[u]; ~i; i = edge[i].next) {
v = edge[i].v;
if(v == pre) continue;
dfs1(v, u, k + 1);
siz[u] += siz[v];
if(son[u] == -1 || siz[son[u]] < siz[v]) son[u] = v;
}
}
void dfs2(int u, int tp) {
int v;
tid[u] = ++ id;
rk[tid[u]] = u;
top[u] = tp;
if(son[u] == -1) return;
dfs2(son[u], tp);
for(int i = head[u]; ~i; i = edge[i].next) {
v = edge[i].v;
if(v == fa[u] || v == son[u]) continue;
dfs2(v, v);
}
}
inline void pushDown(const int& rt) {
if(seg[rt] == -1) return;
seg[rt << 1] = seg[rt << 1 | 1] = seg[rt];
seg[rt] = -1;
}
void update1(const int& p, const int& w, int l, int r, int rt) {
if(l == r) {
seg[rt] = w;
return;
}
__mid__;
if(p <= mid) update1(p, w, lson);
else update1(p, w, rson);
}
void update2(const int& L, const int& R, const int& w, int l, int r, int rt) {
if(L <= l && r <= R) {
seg[rt] = w;
return;
}
__mid__;
pushDown(rt);
if(L <= mid) update2(L, R, w, lson);
if(R > mid) update2(L, R, w, rson);
}
int query(const int& p, int l, int r, int rt) {
if(l == r) { return seg[rt]; }
__mid__;
pushDown(rt);
if(p <= mid) return query(p, lson);
else return query(p, rson);
}
void findPath(int u, int v, int w) {
int ret = 0, f1 = top[u], f2 = top[v];
while(f1 != f2) {
if(dep[f1] < dep[f2]) {
swap(u, v);
swap(f1, f2);
}
update2(tid[f1], tid[u], w, 1, id, 1);
u = fa[f1], f1 = top[u];
}
if(u == v) return;
if(dep[u] > dep[v]) swap(u, v);
update2(tid[son[u]], tid[v], w, 1, id, 1);
}
int main() {
#ifndef ONLINE_JUDGE
FIN;
#endif // ONLINE_JUDGE
int u, v, w, oper;
scanf("%d", &T);
while(T --) {
init();
scanf("%d %d", &N, &M);
for(int i = 1; i <= N - 1; i ++) {
scanf("%d %d %d", &u, &v, &w);
E[i][0] = u, E[i][1] = v, E[i][2] = w;
add_edge(u, v);
add_edge(v, u);
}
dfs1(1, -1, 1);
dfs2(1, 1);
memset(seg, -1, sizeof(seg));
for(int i = 1; i <= N - 1; i ++) {
if(dep[E[i][0]] > dep[E[i][1]]) swap(E[i][0], E[i][1]);
update1(tid[E[i][1]], E[i][2], 1, id, 1);
}
update1(tid[1], -1, 1, id, 1);
while(M --) {
scanf("%d", &oper);
if(oper == 0) {
scanf("%d", &u);
int res = 0, ret;
for(int i = head[u]; ~i; i = edge[i].next) {
v = edge[i].v;
if(dep[v] < dep[u]) v = u;
ret = query(tid[v], 1, id, 1);
res ^= ret;
}
if(res == 1) printf("Girls win!\n");
else printf("Boys win!\n");
} else {
scanf("%d %d %d", &u, &v, &w);
findPath(u, v, w);
}
}
}
return 0;
}