BZOJ 2819: Nim 树状数组+lca+dfs序

Time Limit: 20 Sec Memory Limit: 128 MB
Submit: 2998 Solved: 1120

Description

著名游戏设计师vfleaking,最近迷上了Nim。普通的Nim游戏为:两个人进行游戏,N堆石子,每回合可以取其中某一堆的任意多个,可以取完,但不可以不取。谁不能取谁输。这个游戏是有必胜策略的。于是vfleaking决定写一个玩Nim游戏的平台来坑玩家。
为了设计漂亮一点的初始局面,vfleaking用以下方式来找灵感:拿出很多石子,把它们聚成一堆一堆的,对每一堆编号1,2,3,4,…n,在堆与堆间连边,没有自环与重边,从任意堆到任意堆都只有唯一一条路径可到达。然后他不停地进行如下操作:

1.随机选两个堆v,u,询问若在v到u间的路径上的石子堆中玩Nim游戏,是否有必胜策略,如果有,vfleaking将会考虑将这些石子堆作为初始局面之一,用来坑玩家。
2.把堆v中的石子数变为k。

由于vfleaking太懒了,他懒得自己动手了。请写个程序帮帮他吧。

Input

第一行一个数n,表示有多少堆石子。
接下来的一行,第i个数表示第i堆里有多少石子。
接下来n-1行,每行两个数v,u,代表v,u间有一条边直接相连。
接下来一个数q,代表操作的个数。
接下来q行,每行开始有一个字符:
如果是Q,那么后面有两个数v,u,询问若在v到u间的路径上的石子堆中玩Nim游戏,是否有必胜策略。
如果是C,那么后面有两个数v,k,代表把堆v中的石子数变为k。

对于100%的数据:
1≤N≤500000, 1≤Q≤500000, 0≤任何时候每堆石子的个数≤32767
其中有30%的数据:
石子堆组成了一条链,这3个点会导致你DFS时爆栈(也许你不用DFS?)。其它的数据DFS目测不会爆。

注意:石子数的范围是0到INT_MAX

Output

对于每个Q,输出一行Yes或No,代表对询问的回答。

Sample Input

【样例输入】

5

1 3 5 2 5

1 5

3 5

2 5

1 4

6

Q 1 2

Q 3 5

C 3 7

Q 1 2

Q 2 4

Q 5 3

Sample Output

Yes

No

Yes

Yes

Yes

HINT

Source

湖北省队互测


说实话,dfs序还真的是个好东西,我们先dfs一遍求出dfs序,求出fa,然后每次我们查询的时候,先倍增求x和y的lca,求得lca之后,显然我们的答案是x到根的异或和异或上y到根的异或和异或上lca的值,那么我们已经有这个方向了,接下来怎么处理修改操作呢?
显然,我们的dfs序就可以用来解决这个问题,我们记录一个dfs序之后,用in去修改一下,再用一个out+1去修改一下,不久只修改到in到out这一段区间的值了吗(也就是这个点的子树,因为仔细一想,修改一个点,影响其他点到根的异或和,而这些点只可能是这个点的子树,换句话说,修改一个点,只会修改到这个点的子树,而我们修改in的时候把后面的所有全部修改了,修改out+1的时候又把从out+1开始的所有点全部修改回来了,也就是说这些点都是不会被影响到的,请注意异或的性质,A^B^B = A),于是我们就可以愉快地解决啦

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int n, m, tail, timer ,in[500005], out[500005], a[500005], C[500005], pow[20], head[500005], dep[500005], fa[500005][20];;
struct line{ int to, nxt; } line[1000005];
void add_line( int from, int to ) { line[++tail].nxt = head[from]; head[from] = tail; line[tail].to = to; }
void modify( int x, int v ) { for( ; x <= n; x += x & -x ) C[x] ^= v; }
int query( int x ) { int tmp = 0; for( ; x; x -= x & -x ) tmp ^= C[x]; return tmp; }
void dfs( int u ) {
    for( register int i = 1; i <= 18; i++ )
    if( dep[u] >= pow[i] )
        fa[u][i] = fa[fa[u][ i - 1 ]][ i - 1 ];
    else break;
    in[u] = ++timer;
    for( register int i = head[u]; i; i = line[i].nxt ) {
        int v = line[i].to;
        if( v == fa[u][0] ) continue;
        dep[v] = dep[u] + 1;
        fa[v][0] = u;
        dfs(v);
    }
    out[u] = timer;
}
int lca( int x, int y ) {
    if( dep[x] < dep[y] )swap( x, y );
    int tmp = dep[x] - dep[y];
    for( register int i = 0; i <= 18; i++ ) 
        if( pow[i] & tmp ) x = fa[x][i]; 
    for( register int i = 18; i >= 0; i-- ) 
        if( fa[x][i] != fa[y][i] ) x = fa[x][i], y = fa[y][i];
    if( x == y )return x;
    return fa[x][0];
}
int main( ) {
    pow[0] = 1; for( register int i = 1; i < 20; i++ ) pow[i] = pow[ i - 1 ] << 1;
    scanf( "%d", &n );
    for( register int i = 1; i <= n; i++ ) scanf( "%d", &a[i] );
    for( register int i = 1; i <= n - 1; i++ ) { int ff, tt;
        scanf( "%d%d", &ff, &tt );
        add_line( ff, tt );add_line( tt, ff );
    }
    dfs(1);
    for( register int i = 1; i <= n; i++ ) modify( in[i], a[i] ), modify( out[i] + 1, a[i] );
    scanf( "%d", &m );
    for( register int i = 1; i <= m; i++ ) {
        char ch[10]; int x, y;
        scanf( "%s", ch );
        scanf( "%d%d", &x, &y );
        if( ch[0] == 'Q' ) {
            int fat = lca( x, y );
            int tmp = query( in[x] ) ^ query( in[y] ) ^ a[fat];
            if( tmp ) printf( "Yes\n" );
            else      printf( "No\n" );
        } else {
            modify( in[x], a[x] ); modify( out[x] + 1, a[x] );
            a[x] = y;
            modify( in[x], y ); modify( out[x] + 1, y ); 
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值