【树链剖分】LGP3613 睡觉困难综合征

原题地址

【题目大意】
不想打了。

【题目分析】
每一位不会互相影响,可以把每一位分开考虑。
还要用unsigned long long。

【解题思路】

将每一位分开以后修改好啊,直接改就好了。
询问的时候算出来每一位填0,1经过这条链的变换之后得到的值。

我们发现从高位往低位走的时候,如果这一位填0可以得到1,那么填0一定是最优的。

否则如果可以填1,就把这一位填为1。

复杂度是 nklog2n (树剖)或者 nklogn (LCT),只能通过50%的数据

然后我们决定把这个k搞掉。
只要把k位一起算就可以了,多维护几个值就好。

复杂度是 O(nklog2n) (树剖)或者 O(nklogn) (LCT)

【代码】

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

typedef unsigned long long ULL;
const ULL mx=0-1;
const int MAXN=2e5+10;
int n,m,k,tot,sz;
int opt[MAXN],siz[MAXN],dep[MAXN],q[MAXN],pos[MAXN];
int head[MAXN],top[MAXN],son[MAXN],fa[MAXN];
ULL val[MAXN],cf[MAXN];

struct Tway
{
    int v,nex;
};
Tway e[MAXN<<1];

struct Tree
{
    ULL v0,v1,w0,w1;
};
Tree tree[MAXN<<2],ans0[MAXN],ans1[MAXN];

inline void add(int u,int v)
{
    ++tot;
    e[tot].v=v;e[tot].nex=head[u];head[u]=tot;
}

inline void dfs0(int u,int f)
{
    siz[u]=1;dep[u]=dep[f]+1;
    for(int i=head[u];i;i=e[i].nex)
    {
        int v=e[i].v;
        if(v==f)
            continue;
        fa[v]=u;
        dfs0(v,u);
        siz[u]+=siz[v];
        if(siz[v]>siz[son[u]])
            son[u]=v;
    }
}

inline void dfs1(int u,int tp)
{
//  printf("%d\n",u);
    top[u]=tp;pos[u]=++sz;q[sz]=u;
    if(!son[u])
        return;
    dfs1(son[u],tp);
    for(int i=head[u];i;i=e[i].nex)
    {
        int v=e[i].v;
        if(v!=fa[u] && v!=son[u])
            dfs1(v,v);
    }
}

inline ULL calc(ULL num,int u)
{
    if(opt[u]==1)   return num&val[u];
    if(opt[u]==2)   return num|val[u];
    if(opt[u]==3)   return num^val[u];
}

inline Tree update(Tree l,Tree r)
{
    Tree tmp;
    tmp.v0=tmp.v1=tmp.w0=tmp.w1=0;
    tmp.v0=(l.v0 & r.v1) | ((~l.v0)&r.v0);
    tmp.v1=(l.v1 & r.v1) | ((~l.v1)&r.v0);
    tmp.w0=(r.w0 & l.w1) | ((~r.w0)&l.w0);
    tmp.w1=(r.w1 & l.w1) | ((~r.w1)&l.w0);//typed wrong
    return tmp;
}

inline void pushup(int rt)
{
    tree[rt]=update(tree[rt<<1],tree[rt<<1|1]);
}

inline void build(int rt,int l,int r)
{
    if(l==r)
    {
        int tmp=q[l];
        tree[rt].v0=tree[rt].w0=calc(0,tmp);
        tree[rt].v1=tree[rt].w1=calc(mx,tmp);
        return;
    }
    int mid=(l+r)>>1;
    build(rt<<1,l,mid);
    build(rt<<1|1,mid+1,r);
    pushup(rt);
}

inline void changep(int rt,int l,int r,int p)
{
    if(l==r && l==p)
    {
        int tmp=q[l];
        tree[rt].v0=tree[rt].w0=calc(0,tmp);
        tree[rt].v1=tree[rt].w1=calc(mx,tmp);
        return;
    }
    int mid=(l+r)>>1;
    if(p<=mid)
        changep(rt<<1,l,mid,p);
    else
        changep(rt<<1|1,mid+1,r,p);
    pushup(rt);
}

inline Tree query(int rt,int l,int r,int L,int R)
{
    if(L<=l && r<=R)
        return tree[rt];
    int mid=(l+r)>>1;
    Tree ret;bool flag=false;
    if(L<=mid)
        ret=query(rt<<1,l,mid,L,R),flag=true;
    if(R>mid)
        if(flag)
            ret=update(ret,query(rt<<1|1,mid+1,r,L,R));
        else
            ret=query(rt<<1|1,mid+1,r,L,R);
    return ret;
}

inline Tree solve(int x,int y)
{
    int cnt0=0,cnt1=0;
    while(top[x]!=top[y])
    {
        if(dep[top[x]]>dep[top[y]])
        {
            ans0[++cnt0]=query(1,1,n,pos[top[x]],pos[x]);
            x=fa[top[x]];
        }
        else
        {
            ans1[++cnt1]=query(1,1,n,pos[top[y]],top[y]);
            y=fa[top[y]];
        }
    }
    if(dep[x]>=dep[y])
        ans0[++cnt0]=query(1,1,n,pos[y],pos[x]);
    else
        ans1[++cnt1]=query(1,1,n,pos[x],pos[y]);
    for(int i=1;i<=cnt0;++i)
        swap(ans0[i].v0,ans0[i].w0),swap(ans0[i].v1,ans0[i].w1);//swap false first

    Tree sum;
    if(cnt0)
    {
        sum=ans0[1];
        for(int i=2;i<=cnt0;++i)
            sum=update(sum,ans0[i]);
        if(cnt1)
            sum=update(sum,ans1[cnt1]);
    }
    else
        sum=ans1[cnt1];
    for(int i=cnt1-1;i>=1;--i)
        sum=update(sum,ans1[i]);
    printf("%d %d %d %d\n",sum.v0,sum.v1,sum.w0,sum.w1);
    return sum;
}

int main()
{
    freopen("LGP3613.in","r",stdin);
    freopen("LGP3613.out","w",stdout);

    scanf("%d%d%d",&n,&m,&k);

    cf[0]=1;
    for(int i=1;i<=k-1;++i)
        cf[i]=(cf[i-1]<<1);
    for(int i=1;i<=n;++i)
        scanf("%d%llu",&opt[i],&val[i]);
    for(int i=1;i<n;++i)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
    }

    dfs0(1,0);dfs1(1,1);
    build(1,1,n);
    for(int i=1;i<=m;++i)
    {
        int op,u,v;ULL t;
        scanf("%d%d%d%llu",&op,&u,&v,&t);

        if(op&1)
        {
            Tree tmp=solve(u,v);ULL ret=0;
            for(int i=63;i>=0;--i)
            {
                ULL t0=(tmp.v0>>i)&1;
                ULL t1=(tmp.v1>>i)&1;

                if(t0>=t1 || cf[i]>t)
                    ret|=(t0 ? cf[i] : 0);
                else
                {
                    ret|=(t1 ? cf[i] : 0);
                    t-=cf[i];
                }
            }
            printf("%llu\n",ret);
        }
        else
        {
            opt[u]=v;val[u]=t;
            changep(1,1,n,pos[u]);
        }
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值