洛谷 P5556 圣剑护符

小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\n");
            else
            {
                if (query(x,y))
                    printf("YES\n");
                else
                    printf("NO\n");
            }
        }
        else
        {
            scanf("%d%d%d",&x,&y,&z);
            modify(x,y,z);
        }
    }
    return 0;
}
posted @ 2020-09-16 11:08  eee_hoho  阅读(165)  评论(0编辑  收藏  举报