小L和小K面前的圣剑由 (n) 块护符组成,分别编号为 (1,2,ldots , n) ,有 (n-1) 条咒力线连接两块护符,形成了一个树形结构。
经过小L和小K的长时间的研究,他们发现护符之间的相互作用并不复杂。每块护符都有一个属性值,第 (i) 块护符的属性值记为 (v_i) 。这个值的每个二进制位上的 (0) 或 (1) 表示这块护符是否拥有特定属性。所有属性值中相同的二进制位对应的是相同的属性。
对于一系列护符(护符的集合),对于每种特定属性,统计其中包含这一属性的护符数量,如果为偶数,则这一系列护符形成了干涉,最终的属性值对应的二进制位上为 (0) ,如果为奇数则干涉后剩下了一块护符的影响,对应的二进制位为 (1) 。也就是说, 护符集合的属性值为单个护符的属性值的异或和 。 空集的属性值定义为 (0) 。
现在,小L想知道,如果取出两块护符 (x,y) 间的简单路径上的所有护符,能否找到两个不相等的子集,使得两个子集的属性值相同(注意到空集也是路径上所有护符集合的子集)。同时,小K还会将两块护符间的路径上的所有护符取出进行调整,将所有这些护符的属性值在某些相同二进制位上进行修改(即 (0) 变为 (1) , (1) 变为 (0) ),可以看做是将所有这些护符的属性值异或上了一个值。
我们发现询问如果是NO,那么(x,y)路径上的点是线性相关的,所以我们考虑每次插入路径上一个点,如果这个点无法插入,那么答案就是YES,否则就是NO。
而这样子复杂度肯定是不对的,所以我们还需要寻找一些规律。
在当前都是NO的前提下,发现最多插入(31)个数就一定能使线性基插满,再插入就一定插不进去了,所以两个点的路径长度如果大于(31)那么答案肯定是YES。
小于等于(31)的询问枚举每个点暴力插入判断就好了。
需要树剖和线段树维护区间异或和单点查询。
复杂度(O(n31logn))
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
const int N = 1e5;
using namespace std;
int n,p[31],q,edge[N * 2 + 5],nxt[N * 2 + 5],head[N + 5],edge_cnt,dfn[N + 5],dfc,size[N + 5],son[N + 5],top[N + 5],dep[N + 5],fa[N + 5],val[N + 5],ID[N + 5];
char ch[10];
void add_edge(int u,int v)
{
edge[++edge_cnt] = v;
nxt[edge_cnt] = head[u];
head[u] = edge_cnt;
}
struct Seg
{
#define zrt k << 1
#define yrt k << 1 | 1
int tag[N * 4 + 5],sm[N * 4 + 5];
void build(int k,int l,int r)
{
if (l == r)
{
sm[k] = val[ID[l]];
return;
}
int mid = l + r >> 1;
build(zrt,l,mid);
build(yrt,mid + 1,r);
}
void add(int k,int l,int r,int z)
{
tag[k] ^= z;
sm[k] ^= z;
}
void pushdown(int k,int l,int r,int mid)
{
if (tag[k])
{
add(zrt,l,mid,tag[k]);
add(yrt,mid +1,r,tag[k]);
tag[k] = 0;
}
}
void modify(int k,int l,int r,int x,int y,int z)
{
if (l >= x && r <= y)
{
add(k,l,r,z);
return;
}
int mid = l + r >> 1;
pushdown(k,l,r,mid);
if (x <= mid)
modify(zrt,l,mid,x,y,z);
if (y > mid)
modify(yrt,mid + 1,r,x,y,z);
}
int query(int k,int l,int r,int x)
{
if (l == r)
return sm[k];
int mid = l + r >> 1;
pushdown(k,l,r,mid);
if (x <= mid)
return query(zrt,l,mid,x);
else
return query(yrt,mid + 1,r,x);
}
}tree;
void dfs1(int u,int f)
{
fa[u] = f;
size[u] = 1;
dep[u] = dep[f] + 1;
for (int i = head[u];i;i = nxt[i])
{
int v = edge[i];
if (v == f)
continue;
dfs1(v,u);
size[u] += size[v];
if (size[v] > size[son[u]])
son[u] = v;
}
}
void dfs2(int u,int to)
{
top[u] = to;
dfn[u] = ++dfc;
ID[dfc] = u;
if (son[u])
dfs2(son[u],to);
for (int i = head[u];i;i = nxt[i])
{
int v = edge[i];
if (v == son[u] || v == fa[u])
continue;
dfs2(v,v);
}
}
int lca(int x,int y)
{
while (top[x] != top[y])
{
if (dep[top[x]] < dep[top[y]])
swap(x,y);
x = fa[top[x]];
}
if (dep[x] > dep[y])
swap(x,y);
return x;
}
int dist(int x,int y)
{
return dep[x] + dep[y] - 2 * dep[lca(x,y)];
}
bool ins(int x)
{
if (!x)
return 0;
for (int i = 30;i >= 0;i--)
if (x >> i & 1)
{
if (!p[i])
{
p[i] = x;
return 1;
}
x ^= p[i];
}
return 0;
}
bool query(int x,int y)
{
memset(p,0,sizeof(p));
while (top[x] != top[y])
{
if (dep[top[x]] < dep[top[y]])
swap(x,y);
for (int i = dfn[top[x]];i <= dfn[x];i++)
if (!ins(tree.query(1,1,n,i)))
return 1;
x = fa[top[x]];
}
if (dfn[x] > dfn[y])
swap(x,y);
for (int i = dfn[x];i <= dfn[y];i++)
if (!ins(tree.query(1,1,n,i)))
return 1;
return 0;
}
void modify(int x,int y,int z)
{
while (top[x] != top[y])
{
if (dep[top[x]] < dep[top[y]])
swap(x,y);
tree.modify(1,1,n,dfn[top[x]],dfn[x],z);
x = fa[top[x]];
}
if (dfn[x] > dfn[y])
swap(x,y);
tree.modify(1,1,n,dfn[x],dfn[y],z);
}
int main()
{
//freopen("p5556.in","r",stdin);
//freopen("a1.out","w",stdout);
scanf("%d%d",&n,&q);
int u,v;
for (int i = 1;i <= n;i++)
scanf("%d",&val[i]);
for (int i = 1;i < n;i++)
{
scanf("%d%d",&u,&v);
add_edge(u,v);
add_edge(v,u);
}
dfs1(1,0);
dfs2(1,1);
tree.build(1,1,n);
int x,y,z;
while (q--)
{
scanf("%s",ch + 1);
if (ch[1] == 'Q')
{
scanf("%d%d",&x,&y);
if (dist(x,y) > 31)
printf("YES
");
else
{
if (query(x,y))
printf("YES
");
else
printf("NO
");
}
}
else
{
scanf("%d%d%d",&x,&y,&z);
modify(x,y,z);
}
}
return 0;
}