洛谷P3613:睡觉困难综合征 (LCT+二进制压位)

题目传送门:https://www.luogu.org/problemnew/show/P3613


题目分析:yuno好可爱呀!!!

由于不同的位运算之间不满足结合律,所以我们不能将后面的操作合并。又因为位运算时每一位是独立的,我们不妨考虑用LCT维护某一位开始是0,1时,经过这条链后会变成多少。询问时从高位向低位贪心:如果某一位可以选0或1,且选1比选0优,就选1,否则选0;还要注意最高位限制,有点像数位DP。

但每一位单独维护,时间是 O(nlog(n)k) 的,明显超时,所以我们需要一些奇技淫巧。我们在节点上只存两个数f0,f1,分别代表初始值为 0 2k1时,最后出来的数是多少。合并Splay左右子树信息时,用左子树的f0&一下右子树的f1,因为0进左子树后变成f0,f0中的1进右子树就变成了右子树的f1;然后将左子树f0中的0和1取反,&一下右子树的f0,原因同上。对f1也进行这样的操作,就能在 O(1) 的时间进行信息合并。注意,LCT有翻转操作,所以还要维护翻转后的信息g0,g1。由于询问时还是要一位一位地贪心,时间复杂度 O(nlog(n)+nk)

由于我已经将近一个月没有打过LCT了,所以一个月前我切LCT像切菜一样,一个月后我切LCT像剁手一样。这道题我是2A的,原因是询问时,我Evert(x),Access(y)后,忘了Splay(Node[x])QAQ。


CODE:

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;

const int maxn=100100;
typedef unsigned long long ULL;
ULL Max;

ULL Get(ULL x,ULL y,int op)
{
    if (op==1) return (x&y);
    if (op==2) return (x|y);
    return (x^y);
}

ULL Rev(ULL x)
{
    return (Max^x);
}

struct Tnode
{
    ULL f0,f1,g0,g1,val;
    int opt,path_parent;
    bool flip;
    Tnode *son[2],*fa;
    int Get_d() { return (fa->son[1]==this); }
    void Connect(Tnode *P,int d) { (son[d]=P)->fa=this; }
    void Push_down()
    {
        if (flip)
        {
            swap(son[0],son[1]);
            if (son[0])
            {
                swap(son[0]->f0,son[0]->g0);
                swap(son[0]->f1,son[0]->g1);
                son[0]->flip^=1;
            }
            if (son[1])
            {
                swap(son[1]->f0,son[1]->g0);
                swap(son[1]->f1,son[1]->g1);
                son[1]->flip^=1;
            }
            flip=false;
        }
    }
    void Up()
    {
        if (son[0]) f0=son[0]->f0,f1=son[0]->f1;
        else f0=0,f1=Max;
        f0=Get(f0,val,opt);
        f1=Get(f1,val,opt);
        if (son[1])
        {
            ULL temp=(f0&son[1]->f1);
            temp|=(Rev(f0)&son[1]->f0);
            f0=temp;
            temp=(f1&son[1]->f1);
            temp|=(Rev(f1)&son[1]->f0);
            f1=temp;
        }

        if (son[1]) g0=son[1]->g0,g1=son[1]->g1;
        else g0=0,g1=Max;
        g0=Get(g0,val,opt);
        g1=Get(g1,val,opt);
        if (son[0])
        {
            ULL temp=(g0&son[0]->g1);
            temp|=(Rev(g0)&son[0]->g0);
            g0=temp;
            temp=(g1&son[0]->g1);
            temp|=(Rev(g1)&son[0]->g0);
            g1=temp;
        }
    }
} tree[maxn];
Tnode *Node[maxn];
int cur=-1;

int n,m,k;

Tnode *New_node(int op,ULL v)
{
    cur++;
    tree[cur].val=v;
    tree[cur].opt=op;
    tree[cur].path_parent=0;
    tree[cur].flip=false;
    tree[cur].f0=tree[cur].g0=Get(0,v,op);
    tree[cur].f1=tree[cur].g1=Get(Max,v,op);
    tree[cur].fa=tree[cur].son[0]=tree[cur].son[1]=NULL;
    return tree+cur;
}

void Push(Tnode *P)
{
    if (!P) return;
    Push(P->fa);
    P->Push_down();
}

void Zig(Tnode *P)
{
    int d=P->Get_d();
    Tnode *F=P->fa;
    if (P->son[!d]) F->Connect(P->son[!d],d);
    else F->son[d]=NULL;
    if (F->fa) F->fa->Connect(P, F->Get_d() );
    else P->fa=NULL;
    P->Connect(F,!d);
    F->Up();
    P->path_parent=F->path_parent;
    F->path_parent=0;
}

void Splay(Tnode *P)
{
    Push(P);
    Tnode *F;
    while (P->fa)
    {
        F=P->fa;
        if (F->fa) ( P->Get_d()^F->Get_d() )? Zig(P):Zig(F);
        Zig(P);
    }
    P->Up();
}

void Down(int x)
{
    Splay(Node[x]);
    Tnode *&tag=Node[x]->son[1];
    if (tag)
    {
        tag->fa=NULL;
        tag->path_parent=x;
        tag=NULL;
        Node[x]->Up();
    }
}

void Access(int x)
{
    Down(x);
    Splay(Node[x]);
    int y=Node[x]->path_parent;
    while (y)
    {
        Down(y);
        Splay(Node[y]);
        Node[y]->Connect(Node[x],1);
        Node[y]->Up();
        Node[x]->path_parent=0;
        x=y;
        y=Node[x]->path_parent;
    }
}

void Evert(int x)
{
    Access(x);
    Splay(Node[x]);
    Node[x]->flip^=1;
    swap(Node[x]->f0,Node[x]->g0);
    swap(Node[x]->f1,Node[x]->g1);
}

void Link(int x,int y)
{
    Evert(x);
    Splay(Node[x]);
    Node[x]->path_parent=y;
}

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

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

    Max=0;
    ULL temp=1;
    for (int i=0; i<k; i++)
    {
        Max|=temp;
        if (i<k-1) temp<<=1;
    }

    for (int i=1; i<=n; i++)
    {
        ULL v;
        int op;
        scanf("%d%llu",&op,&v);
        Node[i]=New_node(op,v);
    }

    for (int i=1; i<n; i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        Link(x,y);
    }

    for (int i=1; i<=m; i++)
    {
        int id,x,y;
        ULL z;
        scanf("%d%d%d%llu",&id,&x,&y,&z);
        if (id==1)
        {
            Evert(x);
            Access(y);
            Splay(Node[x]); //!!!!!
            ULL v=temp,p=Node[x]->f0,q=Node[x]->f1,ans=0;
            bool High=false;
            while (v)
            {
                if ( (z&v) || High )
                {
                    if ( (q&v)>(p&v) ) ans|=(q&v);
                    else ans|=(p&v),High=true;
                }
                else ans|=(p&v);
                v>>=1;
            }
            printf("%llu\n",ans);
        }
        else
        {
            Splay(Node[x]);
            Node[x]->opt=y;
            Node[x]->val=z;
            Node[x]->Up();
        }
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值