[hdu 5963 朋友] 博弈SG函数+树链剖分

[hdu 5963 朋友] 博弈SG函数+树链剖分

1. 题目链接

[hdu 5963 朋友]

2. 题意描述

中文题目,直接copy题面了。


Problem Description

B君在围观一群男生和一群女生玩游戏,具体来说游戏是这样的:
给出一棵n个节点的树,这棵树的每条边有一个权值,这个权值只可能是0或1。 在一局游戏开始时,会确定一个节点作为根。接下来从女生开始,双方轮流进行 操作。
当一方操作时,他们需要先选择一个不为根的点,满足该点到其父亲的边权为1; 然后找出这个点到根节点的简单路径,将路径上所有边的权值翻转(即0变成1,1 变成0 )。
当一方无法操作时(即所有边的边权均为0),另一方就获得了胜利。
如果在双方均采用最优策略的情况下,女生会获胜,则输出“Girls win!”,否则输 出“Boys win!”。
为了让游戏更有趣味性,在每局之间可能会有修改边权的操作,而且每局游戏指 定的根节点也可能是不同的。
具体来说,修改边权和进行游戏的操作一共有m个,具体如下:

  • “0 x”表示询问对于当前的树,如果以x为根节点开始游戏,哪方会获得胜利。
  • “1 x y z ”表示将x和y之间的边的边权修改为z。
    B君当然知道怎么做啦!但是他想考考你。

Input

包含至多5组测试数据。
第一行有一个正整数,表示数据的组数。
接下来每组数据第一行,有二个空格隔开的正整数 n,m ,分别表示点的个数,操 作个数。保证 n,m<40000
接下来n-1行,每行三个整数x,y,z,表示树的一条边。保证 1<x<n,1<y<n,0z1
接下来m行,每行一个操作,含义如前所述。保证一定只会出现前文中提到的两 种格式。
对于操作0,保证 1<=x<=n ;对于操作1,保证 1xn,1yn,0z1 ,保证树上存在一条边连接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!


3. 解题思路

首先呢。对于操作 0  x ,显然是一个博弈问题。可以先在一条树链上面(即先考虑顶点 x 只有一条边与其相连的情况),按照SG函数,手算一下, 找出结论: 在一条树链上面,先手赢当且仅当与x相连的边权为 1 。这个很重要的。
然后,很容易推算,对于多条树链的情况,很容易得出结论:先手赢当且仅当与x相连的所有边权异或和 1
至此,题目已经解决了一大部分。
然后,我们的任务就是统计顶点x所有相连边的异或和,还有更新顶点 u 到顶点v的路径上的边权。
对于操作二(更新边权)。我们容易想到的做法就是,树链剖分。
所以问题又转化为一个区间覆盖更新,然后单点查询的一个边权的树链剖分。
对于操作一, 0  x ,就是一个遍历所有跟顶点 x 相连的边,单点查询边权,然后求异或和的过程。【但是,请注意,虽然做法是这么写的, 但是我觉得按道理这个做法应该是TLE的。因为每次询问的复杂度是O(Elog(E))】。

4. 参考代码

#include <bits/stdc++.h>
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;
}
1、资源项目源码均已通过严格测试验证,保证能够正常运; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REaDME.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值