bzoj2819 nim (树上带修改查询路径异或和)

题目简述:

给定一棵树,n个节点,每个节点表示一个石子堆。有m个操作,操作分两种,第一种修改节点中石子数量,第二种查询两个节点路径上的所有石子堆玩nim游戏,是否必胜。

数据范围:n,m<=500000,石子堆数量<=int_max

分析:

首先需要知道,nim游戏的必胜局面是石子堆的异或和不为0。基于以下性质,可以证明。

1.石子全部取完为必败局面,其异或和为0.

2.任意异或和不为0的局面,存在一种取法,能够转化为异或和为0的局面。

3.任意异或和为0的局面,无论如何取,都将转化为异或和不为0的局面。

于是原问题转换为求路径上的石子堆异或和。

方法一:使用链剖求lca,并利用线段树或树状数组维护重链上的异或和,则可以在log2(n)的时间内完成一次查询。

方法二:先做dfs处理,得到一个dfs序列,序列中每个节点出现两次,入栈一次,出栈一次,在任意子树在dfs序列中均为一段连续区间。

对dfs序列使用树状数组维护前缀异或和。

使用倍增处理任意两点的lca。

查询路径(u,v)的异或和,即为getsum(st[u])^getsum(st[v])^stone[lca[u,v]]

其中st[u]表示节点u在dfs序列中第一次出现的位置。

修改节点u的石子数,即可update(st[u],stone[u]),update(ed[u]+1,stone[u]),update(st[u],newvalue),update(ed[u]+1,newvalue)

时间复杂度为O(MlogN).但常数略大。似乎比第一种方法要慢些。

由于担心爆栈,故写了手动栈。

方法一:链剖求lca,使用了bfs。发现一次bfs即可,代替了原来的两次dfs写法。
#include <iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
#define MAXN 500005
int fir[MAXN],nxt[MAXN<<1],edge[MAXN<<1],fa[MAXN],son[MAXN],depth[MAXN],pos[MAXN],maxson[MAXN],sz[MAXN],top[MAXN];
#define lowbit(x) (x&-x)
int stone[MAXN],xorarr[MAXN];
int que[MAXN<<1],head,tail,ecnt;
bool vis[MAXN];
int n,k,q;
char opt[3];
void getnum(int &t)
{
    char c;
    t=0;
    int flag=1;
    while(c=getchar(),(c>'9'||c<'0'))if(c=='-')flag=-1;
    while(c>='0'&&c<='9'){t=t*10+c-48;c=getchar();}
    t*=flag;
}
void adde(int a,int b)
{
    ecnt++;
    edge[ecnt]=b,nxt[ecnt]=fir[a],fir[a]=ecnt;
    ecnt++;
    edge[ecnt]=a,nxt[ecnt]=fir[b],fir[b]=ecnt;
}
void insert(int id,int num)
{
  while(id<=n)
  {
    xorarr[id]^=num;
    id+=lowbit(id);
  }
}
void bfs(int x)
{
    que[tail++]=x;
    depth[x]=1;
    fa[x]=0;
    while(head<tail)
    {
        int t=que[head++];
        for(int i=fir[t];i;i=nxt[i])
        {
            int v=edge[i];
            if(v!=fa[t])
            {
                depth[v]=depth[t]+1;
                fa[v]=t;
                que[tail++]=v;
            }
        }
    }
    for(int i=tail-1;i>=0;i--)
    {
      sz[que[i]]++;
      if(i==0)break;
      sz[fa[que[i]]]+=sz[que[i]];
      if(sz[que[i]]>maxson[fa[que[i]]])
        maxson[fa[que[i]]]=sz[que[i]],son[fa[que[i]]]=que[i];
    }
    int k=0;
    for(int i=0;i<tail;i++)
    {
      int t=que[i];
      if(vis[t]==1)continue;
      top[t]=t;
      while(t)
      {
          vis[t]=1;
          //xorarr[++k]=t;
          insert(++k,stone[t]);
          pos[t]=k;
          t=son[t];
          if(t)top[t]=top[fa[t]];
      }
    }
}

int getsum(int id)
{
    int sum=0;
    while(id>0)
    {
        sum^=xorarr[id];
        id-=lowbit(id);
    }
    return sum;
}
int query(int u,int v)
{
    int tmp=0;
    while(top[v]!=top[u])
    {
    	if(depth[top[v]]>depth[top[u]])swap(u,v);
      tmp^=getsum(pos[top[u]]-1);
      tmp^=getsum(pos[u]);
      u=fa[top[u]];
    }
    if(depth[v]>depth[u])swap(u,v);
    tmp^=getsum(pos[v]-1);
    tmp^=getsum(pos[u]);
    return tmp;
}
void change(int u,int value)
{
    insert(pos[u],stone[u]);
    insert(pos[u],value);
    stone[u]=value;
}
int main()
{
    freopen("nim.in","r",stdin);
    freopen("nim.out","w",stdout);
    int a,b;
    getnum(n);
    for(int i=1;i<=n;i++)
        getnum(stone[i]);
    for(int i=1;i<n;i++)
        getnum(a),getnum(b),adde(a,b);
    bfs(1);
    getnum(q);
    for(int i=1;i<=q;i++)
    {
        scanf("%s",opt);
        //getnum(a),getnum(b);
        scanf("%d %d",&a,&b);
        
        if(opt[0]=='C')
        {
            change(a,b);
        }
        else if(opt[0]=='Q')
            {
                printf((query(a,b)!=0)?"Yes
":"No
");
            }
    }
    return 0;
}

  

//方法二:先dfs序,倍增求lca,dfs采用了非递归写法。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define MAXN 500005
using namespace std;
#define lowbit(x) (x&-x)
int fir[MAXN],edge[MAXN<<1],nxt[MAXN<<1],ecnt,pos,n,q;
char opt[3];
int f[MAXN][20];
int depth[MAXN];
int que[MAXN],head,tail;
int btree[MAXN<<1];
int stone[MAXN];
int stk1[MAXN],stk2[MAXN],top,st[MAXN],ed[MAXN];
bool vis[MAXN];
void getnum(int &t)
{
    t=0;
	char c;
	int flag=1;
	while(c=getchar(),c<'0'||c>'9')if(c=='-')flag=-1;
	while(c<='9'&&c>='0'){t=t*10+c-48;c=getchar();}
	t*=flag;
}
void adde(int a,int b)
{
	ecnt++;
	edge[ecnt]=b,nxt[ecnt]=fir[a],fir[a]=ecnt;
	ecnt++;
	edge[ecnt]=a,nxt[ecnt]=fir[b],fir[b]=ecnt;
}
void dfs(int s)
{
	stk1[++top]=s;
	stk2[top]=fir[s];
	st[s]=++pos;
	depth[s]=1;
	vis[s]=1;
	for(int i=0;i<19;i++)
	f[s][i]=0;
	while(top)
	{
			int i=stk2[top];
			if(i)
			{int v=edge[i];
			stk2[top]=nxt[i];
			if(!vis[v])
			{
				vis[v]=1;
				depth[v]=depth[stk1[top]]+1;
				f[v][0]=stk1[top];
				for(int i=1;i<19;i++)
				f[v][i]=f[f[v][i-1]][i-1];

				stk1[++top]=v;
				st[v]=++pos;
				stk2[top]=fir[v];
			}
			}
			else
				ed[stk1[top--]]=++pos;
	}
}
void update(int x,int value)
{
	while(x<=2*n)
	{
		btree[x]^=value;
		x+=lowbit(x);
	}
}
int getsum(int x)
{
	int sum=0;
	while(x>0)
	{
		sum^=btree[x];
		x-=lowbit(x);
	}
	return sum;
}

int getlca(int a,int b)
{
	if(depth[a]<depth[b])swap(a,b);
	int diff=depth[a]-depth[b];
	for(int i=0;i<20;i++)
	{
		if((diff>>i)&1)
		a=f[a][i];
	}
	if(a==b)return a;
	for(int i=19;i>=0;i--)
	{
		if(f[a][i]!=f[b][i])
		a=f[a][i],b=f[b][i];
	}
	return f[a][0];
}
void change(int pos,int value)
{
	update(st[pos],stone[pos]);
	update(ed[pos],stone[pos]);
	update(st[pos],value);
	update(ed[pos],value);
	stone[pos]=value;
}
int query(int a,int b)
{
    int sum=0;
	int c=getlca(a,b);
	sum=getsum(st[a]);
	sum^=getsum(st[b]);
	sum^=stone[c];
	return sum;
}
 int main()
 {
 	int a,b;
 	getnum(n);
 	for(int i=1;i<=n;i++)
 		getnum(stone[i]);
 	for(int i=1;i<n;i++)
 		getnum(a),getnum(b),adde(a,b);
    dfs(1);
    for(int i=1;i<=n;i++)
 	{
		update(st[i],stone[i]);
		update(ed[i],stone[i]);
	 }
	getnum(q);
	for(int i=1;i<=q;i++)
	{
		scanf("%s",opt);
		getnum(a),getnum(b);
		if(opt[0]=='C')
			change(a,b);
		else {
		    printf(query(a,b)?"Yes
":"No
");
		}
	}
	return 0;
 }

  

原文地址:https://www.cnblogs.com/hefenghhhh/p/9737567.html