洛谷 P3613 睡觉困难综合征(Link-Cut Tree+贪心+一堆位运算)

题目传送门

https://www.luogu.org/problemnew/show/P3613#sub


题解

之前在某tu的博客里看到这题,许久没碰LCT,就翻出来做。

由于上一篇博客的公式敲到我呕心沥血、自废双臂,所以还是没有公式的数据结构题好。

首先考虑一条路径,一个数从那里走一遍,由于二进制里的每一位是独立、互不影响的,于是我们可以每一位分开考虑。

这样只用考虑每一位选0或1从路径经过后的答案即可。明显可以用LCT来做。

当我们读入一个上界时,由于不能超过它,所以每一位不一定取最优,我们从高到低贪心选取,尽量使高位是1且满足要求。低位在高位之后考虑一定更优。

但是这样明显超时。我们分开算每一位导致维护的时间变成k倍。由于互不影响,一起维护也可。于是在LCT操作时搞一堆位运算,然后算答案时直接从每一位拿就行了。

为了方便操作,我们每棵splay维护0和2^k-1经过后的值。由于splay上一棵子树是一段路径,合并子树时先拿左子树和根搞,再从结果中将0提出来和右子树0进入的值&一下,将1提出来和右子树1进入的值&一下,就能得到正确的结果。

这里要注意左右儿子为空的情况,虽然有点烦,但还是很清晰的。

由于路径有方向,所以要维护双向的信息。每次rev操作改变自己信息,但不仅仅改变儿子位置,也改变了儿子的信息,所以每次根up时要将左右儿子的rev先处理了,而一般rev操作不改变值就没必要。
(ps:这里的down自己是为了父亲的up,所以自己的up要先down儿子,而一般的add已经在down自己时维护儿子信息了,注意区别)

这样的时间复杂度就变成了O(nlogn+m*(k+logn))。

注意2^64已经爆了ULL,所以别试着把它写进代码里。


代码

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <cstring>
#define maxn 100010

using namespace std;

typedef unsigned long long ULL;
int n, m, k, cnt;

ULL INF;

struct Tnode{
    ULL val, zero, one, rzero, rone;
    int type, parent;
    bool rev;
    Tnode *son[2], *fa;
    int Get_d(){return fa->son[1] == this;}
    void Connect(Tnode *now, int d){(son[d] = now)->fa = this;}

    void Down(){
        if(rev){
            swap(zero, rzero);
            swap(one, rone);
            swap(son[0], son[1]);
            if(son[0])  son[0]->rev ^= 1;
            if(son[1])  son[1]->rev ^= 1;
            rev = false;
        }
    }

    void Up(){
        ULL A0, A1, B0, B1;

        if(son[0])  son[0]->Down();
        if(son[1])  son[1]->Down();

        if(!son[0]){
            if(type == 1)  A0 = 0ULL, A1 = val;
            else if(type == 2)  A0 = val, A1 = INF;
            else  A0 = val, A1 = INF ^ val;
        }
        else{
            if(type == 1)  A0 = son[0]->zero & val, A1 = son[0]->one & val;
            else if(type == 2)  A0 = son[0]->zero | val, A1 = son[0]->one | val;
            else  A0 = son[0]->zero ^ val, A1 = son[0]->one ^ val;
        }

        if(!son[1])  B0 = 0ULL, B1 = INF;
        else  B0 = son[1]->zero, B1 = son[1]->one;

        zero = ((INF ^ A0) & B0) | (A0 & B1);
        one = (A1 & B1) | ((INF ^ A1) & B0);

        if(!son[1]){
            if(type == 1)  B0 = 0ULL, B1 = val;
            else if(type == 2)  B0 = val, B1 = INF;
            else  B0 = val, B1 = INF ^ val;
        }
        else{
            if(type == 1)  B0 = son[1]->rzero & val, B1 = son[1]->rone & val;
            else if(type == 2)  B0 = son[1]->rzero | val, B1 = son[1]->rone | val;
            else  B0 = son[1]->rzero ^ val, B1 = son[1]->rone ^ val;
        }

        if(!son[0])  A0 = 0ULL, A1 = INF;
        else  A0 = son[0]->rzero, A1 = son[0]->rone;

        rzero = ((INF ^ B0) & A0) | (B0 & A1);
        rone = (B1 & A1) | ((INF ^ B1) & A0);
    }

}tree[maxn], *Node[maxn];


Tnode *NewTnode(int type, ULL val){
    tree[cnt].son[0] = tree[cnt].son[1] = tree[cnt].fa = NULL;
    tree[cnt].parent = 0;
    tree[cnt].val = val;
    tree[cnt].type = type;
    tree[cnt].rev = false;
    return tree+cnt++;
}

void Zig(Tnode *now){
    Tnode *last = now->fa;
    int d = now->Get_d();
    if(now->son[!d])  last->Connect(now->son[!d], d);
    else  last->son[d] = NULL;

    if(last->fa)  last->fa->Connect(now, last->Get_d());
    else  now->fa = NULL;

    now->Connect(last, !d);
    last->Up();
    now->parent = last->parent;
    last->parent = 0;
}

void Splay(Tnode *now){
    Tnode *last;
    while(now->fa){
        last = now->fa;
        if(last->fa)  last->fa->Down();
        last->Down();  now->Down();
        if(last->fa)  (last->Get_d() ^ now->Get_d()) ? Zig(now) : Zig(last);
        Zig(now);
    }
    if(!now->fa)  now->Down();
    now->Up();
}


void Access(int x){

    Splay(Node[x]);
    if(Node[x]->son[1]){
        Node[x]->son[1]->fa = NULL;
        Node[x]->son[1]->parent = x;
        Node[x]->son[1] = NULL;
        Node[x]->Up();
    }

    int y = Node[x]->parent;
    while(y){
        Splay(Node[y]);
        if(Node[y]->son[1]){
            Node[y]->son[1]->fa = NULL;
            Node[y]->son[1]->parent = y;
            Node[y]->son[1] = NULL;
            Node[y]->Up();
        }

        Node[y]->Connect(Node[x], 1);
        Node[x]->parent = 0;
        Node[y]->Up();

        x = y;
        y = Node[x]->parent;
    }
}

void Evert(int x){
    Access(x);
    Splay(Node[x]);
    Node[x]->rev ^= 1;
}

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

void Update(int x, int y, ULL z){
    Splay(Node[x]);
    Node[x]->type = y;
    Node[x]->val = z;
    Node[x]->Up();
}

ULL Query(int x, int y, ULL z){
    Evert(x);
    Access(y);
    Splay(Node[y]);

    ULL ans0 = Node[y]->zero, ans1 = Node[y]->one, ans = 0ULL, num = 0ULL;

    for(int i = k; i >= 0; i--){
        if((1ULL << i) & ans0)  ans |= (1ULL << i);
        else if(((1ULL << i) & ans1) && (num | (1ULL << i)) <= z){
            ans |= (1ULL << i);
            num |= (1ULL << i);
        }
    }
    return ans;
}

int main(){

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

    for(int i = 0; i < k; i++)  INF |= (1ULL << i);

    int op, x, y;
    ULL z;

    for(int i = 1; i <= n; i++){
        scanf("%d%llu", &x, &z);
        Node[i] = NewTnode(x, z);
        Node[i]->Up();
    }

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

    for(int i = 1; i <= m; i++){
        scanf("%d%d%d%llu", &op, &x, &y, &z);
        if(op == 1)  printf("%llu\n", Query(x, y, z));
        else  Update(x, y, z);
    }

    return 0;
}

抽空填坑。

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值