2019ICPC西安邀请赛 E. Tree 树剖 + 线段树

Posted kaka0010

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2019ICPC西安邀请赛 E. Tree 树剖 + 线段树相关的知识,希望对你有一定的参考价值。

原题链接:https://nanti.jisuanke.com/t/39272

题意

有一个树形结构,n个节点,有三种操作

  1. 1到s路径上的节点值或上t
  2. 1到s路径上的节点值与上t
  3. 在1到s路径中的石子中加入一堆t石子,并开始尼姆游戏问先手是否必胜

分析

树上路径带修改,一看就是树剖的题。

尼姆游戏也是个结论,只要全部值异或和为0,先手必败,反之先手必胜

最后处理位运算,我们可以拆成30棵线段树,每颗线段树存放第i位上的值,这样进行或运算时,我们只需要把t在2进制下为1的位在线段树上全部置1,与运算也是同理,将0位在线段树上全部值0,这样就是一个区间修改问题,带个懒惰标记轻松解决。

code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f;
const int N = 1e5 + 10;
const int MOD = 1e9 + 7;
int a[N], b[N];
vector<int> g[N];
int son[N], siz[N], dep[N], fat[N], dfn[N], rnk[N], top[N], tot;
struct node {
    int l, r;
    int sum, tag;
}t[N<<2][31];
void push_up(int u, int id) {
    t[u][id].sum = t[u<<1][id].sum + t[u<<1|1][id].sum;
}
void push_down(int u, int id) {
    if (t[u][id].tag != -1) {
        t[u<<1][id].sum = t[u][id].tag * (t[u<<1][id].r - t[u<<1][id].l + 1);
        t[u<<1|1][id].sum = t[u][id].tag * (t[u<<1|1][id].r - t[u<<1|1][id].l + 1);
        t[u<<1][id].tag = t[u<<1|1][id].tag = t[u][id].tag;
        t[u][id].tag = -1;
    }
}
void build(int u, int id, int l, int r) {
    t[u][id].l = l, t[u][id].r = r;
    t[u][id].tag = -1;
    if (l == r) {
        t[u][id].sum = b[rnk[l]];
        return;
    }
    int mid = (l + r) >> 1;
    build(u<<1, id, l, mid);
    build(u<<1|1, id, mid+1, r);
    push_up(u, id);
}
void add(int u, int id, int pos, int val) {
    if (t[u][id].l == t[u][id].r) {
        t[u][id].sum += val;
        return;
    }
    push_down(u, id);
    int mid = (t[u][id].l + t[u][id].r) >> 1;
    if (pos <= mid) add(u<<1, id, pos, val);
    else add(u<<1|1, id, pos, val);
    push_up(u, id);
}
void modify(int u, int id, int ql, int qr, int val) {
    if (ql <= t[u][id].l && qr >= t[u][id].r) {
        t[u][id].sum = (t[u][id].r-t[u][id].l+1) * val;
        t[u][id].tag = val;
        return;
    }
    push_down(u, id);
    int mid = (t[u][id].l + t[u][id].r) >> 1;
    if (ql <= mid) modify(u<<1, id, ql, qr, val);
    if (qr > mid) modify(u<<1|1, id, ql, qr, val);
    push_up(u, id);
}
int query(int u, int id, int ql, int qr) {
    if (ql <= t[u][id].l && qr >= t[u][id].r) return t[u][id].sum;
    push_down(u, id);
    int mid = (t[u][id].l + t[u][id].r) >> 1;
    int ans = 0;
    if (ql <= mid) ans += query(u<<1, id, ql, qr);
    if (qr > mid) ans += query(u<<1|1, id, ql, qr);
    return ans;
}

void dfs1(int u, int fa) {
    son[u] = -1; siz[u] = 1; dep[u] = dep[fa] + 1; fat[u] = fa;
    for (auto v : g[u]) {
        if (v == fa) continue;
        dfs1(v, u);
        siz[u] += siz[v];
        if (son[u] == -1 || siz[v] > siz[son[u]]) son[u] = v;
    }
}

void dfs2(int u, int t) {
    rnk[dfn[u] = ++tot] = u; top[u] = t;
    if (son[u] == -1) return;
    dfs2(son[u], t);
    for (auto v : g[u]) {
        if (v != son[u] && v != fat[u]) dfs2(v, v);
    }
}

void update(int id, int x, int y, int c) {
    while (top[x] != top[y]) {
        if (dep[top[x]] < dep[top[y]]) swap(x, y);
        modify(1, id, dfn[top[x]], dfn[x], c);
        x = fat[top[x]];
    }
    if (dfn[x] > dfn[y]) swap(x, y);
    modify(1, id, dfn[x], dfn[y], c);
}
int querysum(int id, int x, int y) {
    int ans = 0;
    while (top[x] != top[y]) {
        if (dep[top[x]] < dep[top[y]]) swap(x, y);
        ans += query(1, id, dfn[top[x]], dfn[x]);
        x = fat[top[x]];
    }
    if (dfn[x] > dfn[y]) swap(x, y);
    ans += query(1, id, dfn[x], dfn[y]);
    return ans;
}
void solve () {
    int n, m; cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 0; i < 30; i++) build(1, i, 1, n);

    for (int i = 1; i <= n-1; i++) {
        int u, v; cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    dfs1(1, 0);
    dfs2(1, 1);
    for (int i = 0; i < 30; i++) {
        for (int j = 1; j <= n; j++) {
            b[j] = a[j] >> i & 1;
        }
        build(1, i, 1, n);
    }
    while(m--) {
        int opt, x, y; cin >> opt >> x >> y;
        if (opt == 1) {
            for (int i = 0; i < 30; i++) {
                if (y >> i & 1) update(i, 1, x, 1);
            }
        } else if (opt == 2) {
            for (int i = 0; i < 30; i++) {
                if (!(y >> i & 1)) update(i, 1, x, 0);
            }
        } else {
            ll ans = 0;
            for (int i = 0; i < 30; i++) {
                ans += 1ll*(1 << i) * (querysum(i, 1, x) % 2);
            }
            if (ans == y) cout << "NO" << endl;
            else cout << "YES" << endl;
        }
    }
}
signed main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
#ifdef ACM_LOCAL
    freopen("input", "r", stdin);
    freopen("output", "w", stdout);
    signed test_index_for_debug = 1;
    char acm_local_for_debug = 0;
    do {
        if (acm_local_for_debug == '$') exit(0);
        if (test_index_for_debug > 20)
            throw runtime_error("Check the stdin!!!");
        auto start_clock_for_debug = clock();
        solve();
        auto end_clock_for_debug = clock();
        cout << "Test " << test_index_for_debug << " successful" << endl;
        cerr << "Test " << test_index_for_debug++ << " Run Time: "
             << double(end_clock_for_debug - start_clock_for_debug) / CLOCKS_PER_SEC << "s" << endl;
        cout << "--------------------------------------------------" << endl;
    } while (cin >> acm_local_for_debug && cin.putback(acm_local_for_debug));
#else
    solve();
#endif
    return 0;
}

以上是关于2019ICPC西安邀请赛 E. Tree 树剖 + 线段树的主要内容,如果未能解决你的问题,请参考以下文章

2019 ACM - ICPC 全国邀请赛(西安)题解(9 / 13)

2019 icpc西安邀请赛 点分治

2019 ICPC全国邀请赛(西安)I. Cracking Password(序列检验,BSGS,细节题)

2019ICPC西安邀请赛(计蒜客复现赛)总结

2019icpc西安邀请赛 J And And And (树形dp)

The 2019 ACM-ICPC China Shannxi Provincial Programming Contest (西安邀请赛重现) J. And And And