[bzoj4811] [Ynoi2017]由乃的OJ

题目大意

给定一棵n个节点的树,每个节点上有一个位运算操作(&,|,^)和一个数。
m次操作:1. 修改一个点的操作和数 2. 给定x,y,v,在[0,v]中找一个整数,在树上从x跑到y,到一个节点当前的数对该节点的操作和数进行位运算。问最大可能得到什么数。

n,m≤100000 数的范围小于 264

分析

考虑如何做询问操作。显然可以从高到低逐位确定。现在问题是要求每一位填0或1后从x跑到y,会变成什么。
首先可以想到树链剖分,其次区间答案可合并且不同位不相互影响,所以可以树链剖分+线段树求答案。
但是二进制有64位啊~只要用两个64位整数,分别存64位全是0和全是1的答案,合并时用位运算可以做到O(1)
总时间复杂度就是 O(nlog2n)

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int N=1e5+5,M=262200,L=17;

typedef unsigned long long LL;

int n,m,sb,rt,opt[N],dfn[N],Top[N],tot,h[N],e[N<<1],nxt[N<<1],ch[N],Size[N],fa[N][L],dep[N];

LL v[N];

struct Data
{
    LL s0,s1;
}T1[M],T2[M],ans;

Data operator + (Data a,Data b)
{
    Data c;
    c.s0=((a.s0^(-1))&b.s0)|(a.s0&b.s1);
    c.s1=((a.s1^(-1))&b.s0)|(a.s1&b.s1);
    return c;
}

char c;

LL read()
{
    LL x=0; int sig=1;
    for (c=getchar();c<'0' || c>'9';c=getchar()) if (c=='-') sig=-1;
    for (;c>='0' && c<='9';c=getchar()) x=x*10+c-48;
    return x*sig;
}

void add(int x,int y)
{
    e[++tot]=y; nxt[tot]=h[x]; h[x]=tot;
}

void Init(int x)
{
    dep[x]=dep[fa[x][0]]+1;
    for (int i=h[x];i;i=nxt[i]) if (e[i]!=fa[x][0])
    {
        fa[e[i]][0]=x; Init(e[i]); Size[x]=Size[e[i]]+1;
    }
}

void Change(int l,int r,int g,int x)
{
    if (l==r)
    {
        if (opt[ch[g]]==1) T1[x].s0=0,T1[x].s1=v[ch[g]];
        else if (opt[ch[g]]==2) T1[x].s0=v[ch[g]],T1[x].s1=-1;
        else T1[x].s0=v[ch[g]],T1[x].s1=(-1)^v[ch[g]];
        T2[x]=T1[x];
        return;
    }
    int mid=l+r>>1;
    if (g<=mid) Change(l,mid,g,x<<1);else Change(mid+1,r,g,x<<1|1);
    T1[x]=T1[x<<1]+T1[x<<1|1];
    T2[x]=T2[x<<1|1]+T2[x<<1];
}

void dfs(int x)
{
    dfn[x]=++tot; ch[tot]=x; Top[tot]=rt;
    Change(1,n,tot,1);
    int i,j=0;
    for (i=h[x];i;i=nxt[i]) if (e[i]!=fa[x][0] && (!j || Size[e[i]]>Size[j])) j=e[i];
    if (!j) return;
    dfs(j);
    for (i=h[x];i;i=nxt[i]) if (e[i]!=fa[x][0] && e[i]!=j)
    {
        rt=tot+1;
        dfs(e[i]);
    }
}

int getlca(int x,int y)
{
    if (dep[x]<dep[y]) swap(x,y);
    for (int i=L-1;i>=0;i--) if (dep[fa[x][i]]>=dep[y]) x=fa[x][i];
    if (x==y) return x;
    for (int i=L-1;i>=0;i--) if (fa[x][i]!=fa[y][i])
    {
        x=fa[x][i]; y=fa[y][i];
    }
    return fa[x][0];
}

void ask1(int l,int r,int a,int b,int x)
{
    if (l==a && r==b)
    {
        ans=ans+T1[x]; return;
    }
    int mid=l+r>>1;
    if (b<=mid) ask1(l,mid,a,b,x<<1);
    else if (a>mid) ask1(mid+1,r,a,b,x<<1|1);
    else
    {
        ask1(l,mid,a,mid,x<<1); ask1(mid+1,r,mid+1,b,x<<1|1);
    }
}

void go1(int x,int y)
{
    if (x==y) return;
    x=dfn[x];
    int t=Top[x];
    if (t<=dfn[y]) t=dfn[y]+1;
    go1(fa[ch[t]][0],y);
    ask1(1,n,t,x,1);
}

void ask2(int l,int r,int a,int b,int x)
{
    if (l==a && r==b)
    {
        ans=ans+T2[x]; return;
    }
    int mid=l+r>>1;
    if (b<=mid) ask2(l,mid,a,b,x<<1);
    else if (a>mid) ask2(mid+1,r,a,b,x<<1|1);
    else
    {
        ask2(mid+1,r,mid+1,b,x<<1|1); ask2(l,mid,a,mid,x<<1);
    }
}

void go2(int x,int y)
{
    if (x==y) return;
    x=dfn[x];
    int t=Top[x];
    if (t<=dfn[y]) t=dfn[y]+1;
    ask2(1,n,t,x,1);
    go2(fa[ch[t]][0],y);
}

int main()
{
    n=read(); m=read(); sb=read();
    for (int i=1;i<=n;i++) opt[i]=read(),v[i]=read();
    for (int i=1;i<n;i++)
    {
        int x=read(),y=read();
        add(x,y); add(y,x);
    }
    Init(1); rt=1; tot=0;
    dfs(1);
    for (int j=1;j<L;j++) for (int i=1;i<=n;i++) fa[i][j]=fa[fa[i][j-1]][j-1];
    for (;m--;)
    {
        LL typ=read(),x=read(),y=read(),z=read();
        if (typ==1)
        {
            int lca=getlca(x,y);
            ans.s0=0; ans.s1=-1;
            go2(x,fa[lca][0]); go1(y,lca);
            LL s=0; bool bz=1;
            for (int i=sb-1;i>=0;i--)
            {
                LL st=(LL)1<<i;
                if ((st&z)==0 && bz) s|=(st&ans.s0);else
                {
                    if ((st&ans.s0)>=(st&ans.s1))
                    {
                        s|=(st&ans.s0); bz=0;
                    }else s|=(st&ans.s1);
                }
            }
            printf("%llu\n",s);
        }else
        {
            opt[x]=y; v[x]=z;
            Change(1,n,dfn[x],1);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值