E. Tree(The 2019 ACM-ICPC China Shannxi Provincial Programming Contest)(树链剖分+线段树)

4000ms 262144K
judge:计蒜客

Description

Ming and Hong are playing a simple game called nim game. They have nn piles of stones numbered 1 1 1 to n n n ,the i i i-th pile of stones has a i a_i ai stones. There are n − 1 n - 1 n1 bidirectional roads in total. For any two piles, there is a unique path from one to another. Then they take turns to pick stones, and each time the current player can take arbitrary number of stones from any pile. Of course, the current player should pick at least one stone. Ming always takes the lead. The one who takes the last stone wins this game. Ming and Hong are smart enough so they will make optimal operations every time.

m m m events will take place. Each event has a type called optopt ( the value of opt is among { 1 , 2 , 3 } \lbrace 1,2,3 \rbrace { 1,2,3} ).

If optopt is 1 1 1, modify the numbers of stones of piles on the path from 1 to s s s by the following way: change a i a_i ai to a i ∣ t a_i|t ait. ( s , t s,t s,t will be given).

If optopt is 2 2 2 , modify the numbers of stones of piles on the path from 1 to s s s by the following way: change a i a_i ai to a i & t a_i\& t ai&t. ( s , t s,t s,t will be given).

If optopt is 3 3 3 , they play nim game. Firstly, take the piles on the path from 1 to s s s into consideration. At the same time create a new pile with t stones and take it into consideration. Now they have taken + 1 + 1 +1 piles into consideration. Then they play nim game on these S + 1 S + 1 S+1 piles. You need to figure out if Ming is going to win the game. Amazingly, after playing nim game, the numbers of stones of each pile on the path from 1 1 1 to s s s will return to the original numbers.

Input

Each test file contains a single test case. In each test file:

The first line contains two integers, n n n and m m m.

The next line contains⁡ n n n integers, a i a_i ai.

The next n − 1 n - 1 n1 lines, each line contains two integers, b, c, means there is a bidirectional road between pile b and pile c.

Each of the following m m m lines contains 3 3 3 integers, o p t opt opt, s s s and ⁡ t ⁡t t.

If optopt is 1 1 1, change all the stone numbers of piles on the path from 1 1 1 to s s s in the first way ( a i   t o   a i ∣ t ) (a_i\ to\ a_i|t) (ai to ait)

If optopt is 2 2 2, change all the stone numbers of piles on the path from 1 1 1 to s s s in the second way ( a i   t o   a i & t ) (a_i\ to\ a_i\&t) (ai to ai&t).

If optopt is 3 3 3, we query whether Ming will win when given s s s and t t t ⁡as parameters in this round.

It is guaranteed: 1 ≤ n , m ≤ 1 0 5 1 \le n,m \le 10^5 1n,m105,

0 ≤ a i ≤ 1 0 9 0 \le a_i \le 10^9 0ai109,

1 ≤ o p t ≤ 3 , 1 ≤ s ≤ 1 0 5 , 0 ≤ t ≤ 1 0 9 . 1 \le opt \le 3, 1 \le s \le 10^5, 0 \le t \le 10^9. 1opt3,1s105,0t109..

Output

If optopt is 3 3 3, print a line contains one word “YES” or “NO” (without quotes) , representing whether Ming will win the game.

样例输入

6 6 
0 1 0 3 2 3 
2 1
3 2
4 1
5 3
6 3
1 3 0 
2 3 1
1 1 0 
3 5 0 
2 4 1 
3 3 1

样例输出

YES 
NO

题意

A和B在玩NIM游戏,有 n n n堆石子,编号 1 − n 1-n 1n。两人依次取石子,取得最后一块石子的玩家获胜。

n n n堆石子连成一棵树,有 m m m个事件:

  1. 石子堆 1 1 1到石子堆 s s s的路径上的所有石子堆的石子数与数字 t t t执行按位或运算。即 ( a i   t o   a i ∣ t ) (a_i\ to\ a_i|t) (ai to ait)
  2. 石子堆 1 1 1到石子堆 s s s的路径上的所有石子堆的石子数与数字 t t t执行按位与运算。即 ( a i   t o   a i & t ) (a_i\ to\ a_i\&t) (ai to ai&t)
  3. 两名玩家在石子堆 1 1 1到石子堆 s s s的路径上的石子堆上玩NIM游戏,此外还会新增一个石子数量为 t t t的新堆(新增的堆仅在当前回合有效),询问先手是否必胜。

s s s t t t均由每次操作给出。

题解

首先我们了解NIM游戏的求解方式是把每个石子堆的石子数量做按位异或运算,若结果不等于0,则先手必胜,否则先手必败。

游戏中每次查询都会新增一个新堆,所以我们只需要比较1- s s s路径的异或和是否等于 t t t即可。若相等,则先手必败,否则先手必胜。

因为1- s s s的路径为树上的连续路径,所以可以使树根为节点1,然后使用树链剖分拆分区间,用线段树分别修改和查询每一段区间。

由于我们只需要区间的异或值,异或值是由每个数字相对应的二进制位的1的数量的和决定的:考虑其中一个二进制位,对每个数字的这个二进制位的1的数量求和,当1的数量为偶数时,异或值为0;奇数时为1。

所以我们可以考虑对每个二进制位分别建树,这样每棵线段树只需要维护区间内1的数量即可。可以做到 O ( l o g   n ) O(log\ n) O(log n)时间复杂度内的修改和查询。

代码

// #pragma GCC optimize(2)
#include <bits/stdc++.h>
#define m_p make_pair
#define p_i pair<LL, LL>
#define _for(i, a) for(register LL i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(register LL i = (a), lennn = (b); i <= lennn; ++i)
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n"
#define mem(a, b) memset(a, b, sizeof(a))
#define mem0(a) memset(a, 0, sizeof(a))
#define fil(a, b) fill(a.begin(), a.end(), b);
#define scl(x) scanf("%lld", &x)
#define sc(x) scanf("%d", &x)
#define pf(x) printf("%d\n", x)
#define pfl(x) printf("%lld\n", x)
#define abs(x) ((x) > 0 ? (x) : -(x))
#define PI acos(-1)
#define lowbit(x) (x & (-x))
#define dg if(debug)
using namespace std;
typedef long long LL;
// typedef __int128 LL;
typedef unsigned long long ULL;
const LL maxn = 100005;
const LL maxm = 200005;
const LL maxp = 30;
const LL inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL mod = 1000000007;
const double eps = 1e-8;
const double e = 2.718281828;
LL debug = 0;

inline LL read() {
    
    
	LL x(0), f(1); char ch(getchar());
	while (ch<'0' || ch>'9') {
    
     if (ch == '-') f = -1; ch = getchar(); }
	while (ch >= '0'&&ch <= '9') {
    
     x = x * 10 + ch - '0'; ch = getchar(); }
	return x * f;
}

LL n, m;

struct edge {
    
    
    LL head[maxn], to[maxm], nex[maxm], tot;
    void init() {
    
    
        tot = 0;
        _for(i, n + 1) head[i] = -1;
    }
    void addedge(LL u, LL v) {
    
    
        to[tot] = v;
        nex[tot] = head[u];
        head[u] = tot++;
    }
};

edge E;

LL T[35][maxn << 2];
LL a[maxn];
LL mak[35][maxn << 2];

void push_down(LL s, LL node, LL beg, LL end, LL mid) {
    
    
    if(mak[s][node]) {
    
    
        mak[s][node << 1] = mak[s][node];
        T[s][node << 1] = (mid - beg + 1) * (mak[s][node << 1] == 1);
        mak[s][node << 1 | 1] = mak[s][node];
        T[s][node << 1 | 1] = (end - mid) * (mak[s][node << 1 | 1] == 1);
        mak[s][node] = 0;
    }
}

void update(LL s, LL node, LL beg, LL end, LL l, LL r, LL f) {
    
    
    if(l <= beg && r >= end) {
    
    
        mak[s][node] = f;
        T[s][node] = (end - beg + 1) * (f == 1);
        return;
    }
    LL mid = (beg + end) >> 1;
    push_down(s, node, beg, end, mid);
    if(l <= mid) update(s, node << 1, beg, mid, l, r, f);
    if(r > mid) update(s, node << 1 | 1, mid + 1, end, l, r, f);
    T[s][node] = T[s][node << 1] + T[s][node << 1 | 1];
    return;
}
LL query(LL s, LL node, LL beg, LL end, LL l, LL r) {
    
    
    if (l <= beg && r >= end) return T[s][node];
    LL mid = (beg + end) >> 1;
    push_down(s, node, beg, end, mid);
    LL ans = 0;
    if(mid >= l) ans += query(s, node << 1, beg, mid, l, r);
    if(mid < r) ans += query(s, node << 1 | 1, mid + 1, end, l, r);
    return ans;
}

/*
树链剖分

fa[x] 表示节点 x 在树上的父亲。
dep[x] 表示节点 x 在树上的深度。
siz[x] 表示节点 x 的子树的节点个数。
son[x] 表示节点 x 的 重儿子 。
top[x] 表示节点 x 所在 重链 的顶部节点(深度最小)。
dfn[x] 表示节点 x 的 DFS 序 ,也是其在线段树中的编号。
rnk[x] 表示 DFS 序所对应的节点编号,有 rnk[dfn[x]] = x 。
*/
LL son[maxn], siz[maxn], top[maxn], dfn[maxn], fa[maxn], dep[maxn], cnt, rnk[maxn];

void dfs1(LL u, int f) {
    
    
    siz[u] = 1;
    for(LL i = E.head[u]; ~i; i = E.nex[i]) {
    
    
        LL v = E.to[i];
        if (v != f) {
    
    
            dep[v] = dep[u] + 1;
            fa[v] = u;
            dfs1(v, u);
            siz[u] += siz[v];
            if (siz[v] > siz[son[u]]) son[u] = v;
        }
    }
}

void dfs2(LL u, LL t) {
    
    
    top[u] = t;
    dfn[u] = ++cnt;
    rnk[cnt] = u;
    if (!son[u]) return;
    dfs2(son[u], t);
    for(LL i = E.head[u]; ~i; i = E.nex[i]) {
    
    
        LL v = E.to[i];
        if (v != son[u] && v != fa[u]) dfs2(v, v);
    }
}

inline LL Query(LL p, LL s, LL t) {
    
    
    LL ans = 0;
    while(top[s] != top[t]) {
    
    
        if(dep[top[s]] < dep[top[t]]) swap(s, t);
        ans += query(p, 1, 1, n, dfn[top[s]], dfn[s]);
        s = fa[top[s]];
    }
    if(dep[s] > dep[t]) swap(s, t);
    ans += query(p, 1, 1, n, dfn[s], dfn[t]);
    return ans;
}

inline void Update(LL p, LL s, LL t, LL f) {
    
    
    while(top[s] != top[t]) {
    
    
        if(dep[top[s]] < dep[top[t]]) swap(s, t);
        update(p, 1, 1, n, dfn[top[s]], dfn[s], f);
        s = fa[top[s]];
    }
    if(dep[s] > dep[t]) swap(s, t);
    update(p, 1, 1, n, dfn[s], dfn[t], f);
}

void init() {
    
    
    E.init();
}

void sol() {
    
    
	init();
    _rep(i, 1, n) a[i] = read();
    _for(i, n - 1) {
    
    
        LL u = read(), v = read();
        E.addedge(u, v);
        E.addedge(v, u);
    }
    dfs1(1, -1);
    dfs2(1, 1);
    _rep(i, 1, n) {
    
    
        for(LL j = 0, x = a[i]; j < 34; ++j, x >>= 1) {
    
    
            if(x & 1) Update(j, i, i, 1);
        }
    }
    _for(i, m) {
    
    
        LL op = read(), s = read(), t = read();
        if(op == 1) {
    
    
            for(LL j = 0; j < 34; ++j, t >>= 1) {
    
    
                if(t & 1) Update(j, 1, s, 1);
            }
        }
        else if(op == 2) {
    
    
            for(LL j = 0; j < 34; ++j, t >>= 1) {
    
    
                if(!(t & 1)) Update(j, 1, s, -1);
            }
        }
        else {
    
    
            LL flag = 0;
            for(LL j = 0; j < 34; ++j, t >>= 1) {
    
    
                LL p = Query(j, 1, s);
                if((t & 1) != (p & 1)) {
    
    
                    flag = 1;
                    break;
                }
            }
            printf("%s\n", flag ? "YES":"NO");
        }
    }
}

int main() {
    
    
	//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#ifdef ONLINE_JUDGE
#else
	freopen("in.txt", "r", stdin);
	// debug = 1;
#endif
	time_t beg, end;
	if(debug) beg = clock();

    n = read(), m = read();
    sol();

	if(debug) {
    
    
		end = clock();
		printf("time:%.2fs\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_42856843/article/details/108961188
今日推荐