BZOJ4811: [Ynoi2017]由乃的OJ(洛谷P3613)

本文介绍了一种解决特定区间合并问题的方法,利用树链剖分和位运算技巧来优化算法效率。通过定义两个状态变量p0和p1来表示区间内元素经过运算后的可能结果,并提供了一个具体的实现代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

树链剖分 位运算乱搞

BZOJ题目传送门
洛谷题目传送门

前言
建议先把睡觉困难综合征这道题给做了

题解

和睡觉困难综合征一样,考虑每一位放1和放0的情况。那么我们只需要弄清楚如何合并区间就行了。

定义p0,p1表示全放0/1时经过这一个区间后会变成什么。那么在合并左右区间时就变成了这两个东西:

//y是左区间,z是右区间
x.p0=(y.p0&z.p1)|((~y.p0)&z.p0);
x.p1=(y.p1&z.p1)|((~y.p1)&z.p0);

x.p0放进去后变成两种情况:即变成0或变成1。

变成1:y.p0&z.p1即表示0经过y变成1后再经过z变成的数字。
变成0:(~y.p0)&z.p0表示0经过y变成0后再经过z变成的数字。

那么x.p0就把这两种情况合起来就好了,x.p1也是如此。

左区间进,右区间出 和 右区间进,左区间出是不一样的。存储和查询的时候需要注意。
还有就是p0和p1要开unsigned long long,因为会达到 264 2 64

代码:

#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
using namespace std;
typedef unsigned long long ull;
struct tree{ int l,r; }t[N*4];
struct dt{ ull p1,p0; }tl[N*4],tr[N*4];
struct edge{ int next,to; }ed[N*2];
int n,m,k,p,nd,h[N],tp[N],fa[N],to[N],sz[N],dep[N],in[N],id[N];
ull op[N][2];
inline char readc(){
    static char buf[100000],*l=buf,*r=buf;
    if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
    if (l==r) return EOF; return *l++;
}
inline ull _read(){
    ull x=0; char ch=readc();
    while (!isdigit(ch)) ch=readc();
    while (isdigit(ch)) x=x*10+ch-48,ch=readc();
    return x;
}
void addedge(int x,int y){ ed[++k].next=h[x],ed[k].to=y,h[x]=k; }
void dfs1(int x,int depth){
    sz[x]=1,dep[x]=depth;
    for (int i=h[x];i;i=ed[i].next)
        if (ed[i].to!=fa[x]){
            int v=ed[i].to;
            fa[v]=x,dfs1(v,depth+1),sz[x]+=sz[v];
            if (sz[to[x]]<sz[v]) to[x]=v;
        }
}
void dfs2(int x){
    in[id[x]=++nd]=x;
    if (to[x]) tp[to[x]]=tp[x],dfs2(to[x]);
    for (int i=h[x];i;i=ed[i].next)
        if (ed[i].to!=fa[x]&&ed[i].to!=to[x])
            tp[ed[i].to]=ed[i].to,dfs2(ed[i].to);
}
dt pshp(dt y,dt z){
    dt ret;
    ret.p0=(y.p0&z.p1)|((~y.p0)&z.p0);
    ret.p1=(y.p1&z.p1)|((~y.p1)&z.p0);
    return ret;
}
dt updt(ull op,ull w){
    dt ans;
    if (op==1) ans.p0=0&w,ans.p1=(~0)&w;
    if (op==2) ans.p0=0|w,ans.p1=(~0)|w;
    if (op==3) ans.p0=0^w,ans.p1=(~0)^w;
    return ans;
}
void build(int l,int r,int x){
    t[x].l=l,t[x].r=r;
    if (l==r){ 
        tl[x]=tr[x]=updt(op[in[l]][0],op[in[l]][1]); 
        return; 
    }
    int mid=l+r>>1; 
    build(l,mid,x*2),build(mid+1,r,x*2+1);
    tl[x]=pshp(tl[x*2],tl[x*2+1]);
    tr[x]=pshp(tr[x*2+1],tr[x*2]);
}
void mdfy(int x,int p,ull op,ull w){
    if (t[x].l==t[x].r){ tl[x]=tr[x]=updt(op,w); return; }
    int mid=t[x].l+t[x].r>>1;
    if (p<=mid) mdfy(x*2,p,op,w);
    else mdfy(x*2+1,p,op,w);
    tl[x]=pshp(tl[x*2],tl[x*2+1]);
    tr[x]=pshp(tr[x*2+1],tr[x*2]);
}
dt srch(int x,int l,int r,int fl){
    if (t[x].l>=l&&t[x].r<=r)
        if (fl) return tr[x];
        else return tl[x];
    int mid=t[x].l+t[x].r>>1;
    if (r<=mid) return srch(x*2,l,r,fl);
    else if (l>mid) return srch(x*2+1,l,r,fl);
    else return pshp(srch(x*2+fl,l,r,fl),srch(x*2+1-fl,l,r,fl));
}
dt find(int x,int y){
    dt ans1=updt(3,0),ans2=updt(3,0);
    while (tp[x]!=tp[y])
        if (dep[tp[x]]>=dep[tp[y]]){
            ans1=pshp(ans1,srch(1,id[tp[x]],id[x],1));
            x=fa[tp[x]];
        }
        else{
            ans2=pshp(srch(1,id[tp[y]],id[y],0),ans2);
            y=fa[tp[y]];
        }
    if (dep[x]>=dep[y])
        return pshp(pshp(ans1,srch(1,id[y],id[x],1)),ans2);
    else
        return pshp(pshp(ans1,srch(1,id[x],id[y],0)),ans2);
}
void nswr(int x,int y,ull z){
    dt ans=find(x,y); ull state=0,ret=0;
    for (int i=p;i;i--)
        if (ans.p0&(1ull<<i-1)) ret+=1ull<<i-1;
        else if ((ans.p1&(1ull<<i-1)&&(1ull<<i-1)+state<=z))
            ret+=1ull<<i-1,state+=1ull<<i-1;
    printf("%llu\n",ret);
}
int main(){
    n=_read(),m=_read(),p=_read();
    for (int i=1;i<=n;i++) op[i][0]=_read(),op[i][1]=_read();
    for (int i=1;i<n;i++){
        int u=_read(),v=_read();
        addedge(u,v),addedge(v,u);
    }
    dfs1(1,1),tp[1]=1,dfs2(1),build(1,n,1);
    while (m--){
        int flag=_read(),x=_read(),y=_read(); ull z=_read();
        if (flag==2) mdfy(1,id[x],y,z);
        else nswr(x,y,z);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值