bzoj4811 [Ynoi2017]由乃的OJ 树链剖分+位运算

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4811

因为位运算的结果有可合并性,所以可以树链剖分,线段树维护;

细节很多,特别要注意从左往右运算和从右往左计算是不同的,在不同条件下一定要区分!!!

这篇博客写得很好(我就是模仿它写的):https://blog.csdn.net/a1799342217/article/details/78818480

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef unsigned long long ull;
int const maxn=100005;
int n,m,p,in[maxn],id[maxn],to[maxn],tp[maxn],head[maxn],ct,dep[maxn],fa[maxn],siz[maxn],tim;
ull op[maxn][3];
struct N{
    int to,next;
    N(int t=0,int n=0):to(t),next(n) {}
}edge[maxn<<1];
struct data{ull p0,p1;}tr[maxn<<2],tl[maxn<<2];
struct T{int l,r;}t[maxn<<2];
void add(int x,int y){edge[++ct]=N(y,head[x]);head[x]=ct;}
void dfs1(int x,int f)
{
    fa[x]=f;dep[x]=dep[f]+1;siz[x]=1;
    for(int i=head[x];i;i=edge[i].next)
    {
        int u=edge[i].to;
        if(u==f)continue;
        dfs1(u,x);siz[x]+=siz[u];
        if(siz[u]>siz[to[x]])to[x]=u;
    }
}
void dfs2(int x)
{
    in[id[x]=++tim]=x;
    if(to[x])tp[to[x]]=tp[x],dfs2(to[x]);
    for(int i=head[x];i;i=edge[i].next)
    {
        int u=edge[i].to;
        if(u!=fa[x]&&u!=to[x])tp[u]=u,dfs2(u);
    }
}
data update(ull op,ull w)
{
    data ret;
    if(op==1)ret.p0=(0&w),ret.p1=((~0)&w);
    if(op==2)ret.p0=(0|w),ret.p1=((~0)|w);
    if(op==3)ret.p0=(0^w),ret.p1=((~0)^w);
    return ret;
}
data pushup(data x,data y)
{
    data ret;
    ret.p0=(x.p0&y.p1) | ((~x.p0)&y.p0);
    ret.p1=(x.p1&y.p1) | ((~x.p1)&y.p0);
    return ret;
}
void build(int x,int l,int r)
{
    t[x].l=l;t[x].r=r;
    if(l==r)
    {
        tl[x]=tr[x]=update(op[in[l]][0],op[in[l]][1]);//in[l]而非x! 
        return;
    }
    int mid=((l+r)>>1);
    build(x<<1,l,mid); build(x<<1|1,mid+1,r);
    tl[x]=pushup(tl[x<<1],tl[x<<1|1]);
//    tr[x]=pushup(tr[x<<1],tr[x<<1|1]);
    tr[x]=pushup(tr[x<<1|1],tr[x<<1]);//!!!!!!!!!!
    
}
data sch(int x,int l,int r,int fl)
{
    if(t[x].l>=l&&t[x].r<=r)
        return fl?tr[x]:tl[x];
//      int mid=((l+r)>>1);
    int mid=((t[x].l+t[x].r)>>1);
    if(r<=mid)return sch(x<<1,l,r,fl);
    else if(l>mid)return sch(x<<1|1,l,r,fl);
    else return pushup(sch((x<<1)+fl,l,r,fl),sch((x<<1|1)-fl,l,r,fl));//从左往右或从右往左pushup有不同 
}
data find(int x,int y)
{
    data ans1=update(3,0),ans2=update(3,0);//ans1为x到lca,ans2为y到lca
    while(tp[x]!=tp[y])
    {
//        if(dep[x]>dep[y])
        if(dep[tp[x]]>dep[tp[y]])
        {
            ans1=pushup(ans1,sch(1,id[tp[x]],id[x],1));
            x=fa[tp[x]];
        }
        else
        {
            ans2=pushup(sch(1,id[tp[y]],id[y],0),ans2);
            y=fa[tp[y]];
        }
    } 
    if(dep[x]<dep[y])return pushup(pushup(ans1,sch(1,id[x],id[y],0)),ans2);
    else return pushup(pushup(ans1,sch(1,id[y],id[x],1)),ans2);
}
void query(int x,int y,ull z)//ull
{
    data ans=find(x,y);ull s=0,ret=0;
    for(int i=p-1;i>=0;i--)
    {
        if((1ull<<i)&ans.p0)ret+=(1ull<<i);
        else if(((1ull<<i)&ans.p1)&&s+(1ull<<i)<=z)
            ret+=(1ull<<i),s+=(1ull<<i);
    }
    printf("%llu\n",ret);
}
void modify(int x,int p,ull op,ull w)//ull
{
    if(t[x].l==t[x].r)
    {
        tl[x]=tr[x]=update(op,w);
        return;//
    }
    int mid=((t[x].l+t[x].r)>>1);
    if(p<=mid)modify(x<<1,p,op,w);
    else modify(x<<1|1,p,op,w);
    tl[x]=pushup(tl[x<<1],tl[x<<1|1]);
//  tr[x]=pushup(tr[x<<1],tr[x<<1|1]);
    tr[x]=pushup(tr[x<<1|1],tr[x<<1]);
}
int main()
{
    scanf("%d%d%d",&n,&m,&p);
    for(int i=1;i<=n;i++)scanf("%llu%llu",&op[i][0],&op[i][1]);
    for(int i=1,x,y;i<n;i++)scanf("%d%d",&x,&y),add(x,y),add(y,x);
    dfs1(1,0);tp[1]=1;dfs2(1);build(1,1,n);
    for(int i=1,q,x,y;i<=m;i++)
    {
        ull z;//
        scanf("%d%d%d%llu",&q,&x,&y,&z);
        if(q==1)query(x,y,z);
        else modify(1,id[x],y,z);//
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/Zinn/p/9171531.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值