标签:str dfs序 修改查询 游戏 log 范围 include 连续 name
题目简述:
给定一棵树,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\n":"No\n");
}
}
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\n":"No\n");
}
}
return 0;
}
标签:str dfs序 修改查询 游戏 log 范围 include 连续 name
原文地址:https://www.cnblogs.com/hefenghhhh/p/9737567.html