LPA20020220的博客

Keep on fighting till the end!

[Luogu P3613] 睡觉困难综合征

洛谷传送门

题目描述

因为Claris大佬帮助一周目由乃通过了Deus的题,所以一周目的由乃前往二周目世界找雪辉去了

由于二周目世界被破坏殆尽,所以由乃和雪辉天天都忙着重建世界(其实和MC差不多吧),Deus看到了题问她,总是被告知无可奉告

Deus没办法只能去三周目世界问三周目的由乃OI题。。。

三周目的世界中,因为没有未来日记,所以一切都很正常,由乃天天认真学习。。。

因为Deus天天问由乃OI题,所以由乃去学习了一下OI

由于由乃智商挺高,所以OI学的特别熟练

她在RBOI2016中以第一名的成绩进入省队,参加了NOI2016获得了金牌保送

Deus:这个题怎么做呀?

yuno:这个不是NOI2014的水题吗。。。

Deus:那如果出到树上,多组链询问,带修改呢?

yuno:诶。。。???

Deus:这题叫做睡觉困难综合征哟~

虽然由乃OI很好,但是她基本上不会DS,线段树都只会口胡,比如她NOI2016的分数就是100+100+100+0+100+100。。。NOIP2017的分数是100+0+100+100+0+100

所以她还是只能找你帮她做了。。。

题目描述

由乃这个问题越想越迷糊,已经达到了废寝忘食的地步。结果她发现……晚上睡不着了!只能把自己的一个神经元(我们可以抽象成一个树形结构)拿出来,交给Deus。

这个神经元是一个有n个点的树,每个点的包括一个位运算opt和一个权值x,位运算有&,l,^三种,分别用1,2,3表示。

为了治疗失眠,Deus可以将一些神经递质放在点x上,初始的刺激值是 v0 。然后v依次经过从xy的所有节点,每经过一个点iv就变成v opti xi,所以他想问你,最后到y时,希望得到的刺激值尽可能大,所以最大值的v可以是多少呢?当然由于初始的神经递质的量有限,所以给定的初始值 v0​ 必须是在[0,z]之间。Deus每次都会给你3个数,x,y,z

不过,Deus为了提升治疗效果,可能会对一些神经节点进行微调。在这种情况下,也会给三个数xyz,意思是把x点的操作修改为y,数值改为z

输入输出格式

输入格式:

第一行三个数nmkk的意义是每个点上的数,以及询问中的数值z<2k,。之后n行,每行两个数x,y表示该点的位运算编号以及数值

之后n1行,每行两个数x,y表示xy之间有边相连

之后m行,每行四个数,Q,x,y,z表示这次操作为Q(1为询问,2为更改),xyz意义如题所述。

输出格式:

对于每个操作1,输出到最后可以造成的最大刺激度v。

输入输出样例

输入样例#1:

5 5 3
1 7
2 6
3 7
3 6
3 1
1 2
2 3
3 4
1 5
1 1 4 7
1 1 3 5
2 1 1 3
2 3 3 3
1 1 3 2

输出样例#1:

7
1
5

输入样例#2:

2 2 2
2 2
2 2
1 2
2 2 2 2
1 2 2 2

输出样例#2:

3

说明

对于30%的数据,n,m1

对于另外20%的数据,k5

对于另外20%的数据,位运算只会出现一种

对于100%的数据,0n , m100000 , k64

解题分析

要做这道题, 首先需要做这道题:起床困难综合症 蒟蒻此题题解。然后我们就会发现这道题只是把所有贪心操作搬到了树上, 所以我们只要在合理复杂度内能够提出链来就可以log(k)贪心A题啦….
然后我们发现, 似乎提出一段链的操作LCT可以很容易办到, 那么很容易想到用LCT维护子树中按dfs序操作的一段序列。 但只维护从小到大dfs序的操作还不行, 因为涉及到翻转操作, 翻转时左右顺序正好相反。所以我们同时还需要维护从大到小的操作。

现在我们考虑如何合并左右两端区间操作的结果。 设左半部分每位取0进行操作后的答案为lef.val0, 取1进行操作后的答案为lef.val1, 右半部分每位取0进行操作后的答案为rig.val0, 取1进行操作后的答案为rig.val1, 合并后结果为res。显然, 如果我们每位取0进行左半部分操作后答案一部分将会变成1, 这部分数位在进入右半部分计算时结果为rig.val1的对应数位。同理, 仍为0的一部分进入右半部分计算时结果等于rig.val0的对应数位。 所以我们可得:

res.val0=(~lef.val0&rig.val0)+(lef.val0&rig.val1)

同理可得
res.val1=(~lef.val1&rig.val0)+(lef.val0&rig.val1)

剩下的就是打板贼6了…

PS:此题需要开unsigned long long才能过, 而unsigned long long的输出方法为cout%llu
PPS:博主因为switch()case里面没有break, 导致WA的莫名奇妙(如果switch没有break将会忽略后面所有的case操作, 直接执行完所有情况), 主要是数据正好有20%只有一种操作(操作3), 还A了4个点…博主查了半天都没查出来QAQ…
代码如下:

#include <cstdio>
#include <algorithm>
#include <cctype>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <limits.h>
#define R register
#define IN inline
#define gc getchar()
#define W while
#define MX 200005
#define ll unsigned long long
#define ls tree[now].son[0]
#define rs tree[now].son[1]
#define dad tree[now].fat
template <typename T>
IN void in(T &x)
{
    R bool fu = false;
    x = 0; R char c = gc;
    W (!isdigit(c)) {if(c == '-') fu = true; c = gc;}
    W (isdigit(c))
    x = (x << 1) + (x << 3) + c - 48, c = gc;
    if(fu) x = -x;
}
struct Message
{ll val0, valk;};
struct Node
{
    int son[2], fat;
    bool rev;
    Message lef, rig, ori;
}tree[MX];
int dot, q, top, dgt;
int st[MX];
ll mul[70];
namespace LCT
{
    IN void modify(const int &now, const ll &zero, const ll &kth)
    {
        tree[now].ori.val0 = tree[now].lef.val0 = tree[now].rig.val0 = zero;
        tree[now].ori.valk = tree[now].rig.valk = tree[now].lef.valk = kth;
    }
    IN bool get(const int &now) {return tree[dad].son[1] == now;}
    IN bool nroot(const int &now) {return tree[dad].son[0] == now || tree[dad].son[1] == now;}
    IN Message operator + (const Message &lef, const Message &rig)
    {
        Message ret;
        ret.val0 = (~lef.val0 & rig.val0) | (lef.val0 & rig.valk);
        ret.valk = (~lef.valk & rig.val0) | (lef.valk & rig.valk);
        return ret;
    }
    IN void pushrev(const int &now) 
    {
        tree[now].rev ^= 1;
        std::swap(ls, rs);
        std::swap(tree[now].lef, tree[now].rig);//不要忘了此处需要互换
    }
    IN void pushup(const int &now)
    {
        tree[now].lef = tree[now].rig = tree[now].ori;
        if(ls) tree[now].lef = tree[ls].lef + tree[now].lef, tree[now].rig = tree[now].rig + tree[ls].rig;
        if(rs) tree[now].lef = tree[now].lef + tree[rs].lef, tree[now].rig = tree[rs].rig + tree[now].rig;
    }
    IN void pushdown(const int &now)
    {
        if(tree[now].rev)
        {
            if(ls) pushrev(ls);
            if(rs) pushrev(rs);
            tree[now].rev = false;
        }
    }
    IN void rotate(const int &now)
    {
        R bool dir = get(now);
        R int fa = dad, grand = tree[fa].fat;
        tree[fa].son[dir] = tree[now].son[dir ^ 1];
        tree[tree[now].son[dir ^ 1]].fat = fa;
        if(nroot(fa)) tree[grand].son[get(fa)] = now;
        tree[now].fat = grand;
        tree[now].son[dir ^ 1] = fa;
        tree[fa].fat = now;
        pushup(fa);
    }
    IN void splay(const int &now)
    {
        R int fa, grand, x = now; top = 0;
        st[++top] = x;
        W (nroot(x)) x = tree[x].fat, st[++top] = x;
        W (top) pushdown(st[top--]);
        W (nroot(now))
        {
            fa = dad, grand = tree[fa].fat;
            if(nroot(fa)) rotate(get(now) == get(fa) ? fa : now);
            rotate(now);
        }
        pushup(now);
    }
    IN void access(R int now)
    {
        for (R int x = 0; now; x = now, now = dad)
        splay(now), rs = x, pushup(now);
    }
    IN void makeroot(const int &now)
    {access(now), splay(now), pushrev(now);}
    IN void split(const int &x, const int &y)
    {makeroot(x), access(y), splay(y);}
    IN void link(const int &x, const int &y)
    {makeroot(x); tree[x].fat = y;}
    IN ll query(const int &from, const int &to, const ll &up)
    {
        ll cost = 0, ans = 0;
        split(from, to);
        for (R int i = dgt - 1; ~i; --i)
        {
            if(tree[to].lef.val0 & mul[i]) {ans |= mul[i]; continue;}//split后to的dfs序最大, 所以应该查正向dfs序的答案
            if((tree[to].lef.valk & mul[i]) && up >= (cost | mul[i])) cost |= mul[i], ans |= mul[i];
        }
        return ans;
    }
}
int main(void)
{
    ll a, b, c, typ;
    in(dot), in(q), in(dgt);
    for (R int i = 0; i < dgt; ++i) mul[i] = 1ull << i;
    for (R int i = 1; i <= dot; ++i)
    {
        in(a), in(b);
        switch(a)
        {
            case 1: LCT::modify(i, 0ull, b); break;
            case 2: LCT::modify(i, b, ~0ull); break;
            case 3: LCT::modify(i, b, ~b); break;
        }
    }
    for (R int i = 1; i < dot; ++i)
    {
        in(a), in(b);
        LCT::link(a, b);
    }
    W (q--)
    {
        in(typ);
        if(typ & 1)
        {
            in(a), in(b), in(c);
            printf("%llu\n", LCT::query(a, b, c));
        }
        else
        {
            in(a), in(b), in(c);
            LCT::makeroot(a);
            switch(b)
            {
                case 1: LCT::modify(a, 0ull, c); break;
                case 2: LCT::modify(a, c, ~0ull); break;//不要忘了break!
                case 3: LCT::modify(a, c, ~c); break;
            }
            LCT::pushup(a);
        }
    }
}
阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/LPA20020220/article/details/80696960
个人分类: 贪心 LCT 位运算
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭