原题链接:https://www.luogu.org/problemnew/show/P3613
睡觉困难综合征
题目背景
刚立完Flag我就挂了WC和THUWC。。。
时间限制0.5s,空间限制128MB
因为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上,初始的刺激值是 v 0 v_0 v0 。然后v依次经过从x到y的所有节点,每经过一个点i,v就变成v opti xi,所以他想问你,最后到y时,希望得到的刺激值尽可能大,所以最大值的v可以是多少呢?当然由于初始的神经递质的量有限,所以给定的初始值 v 0 v_0 v0必须是在[0,z]之间。Deus每次都会给你3个数,x,y,z。
不过,Deus为了提升治疗效果,可能会对一些神经节点进行微调。在这种情况下,也会给三个数x,y,z,意思是把x点的操作修改为y,数值改为z。
输入输出格式
输入格式:
第一行三个数n,m,k。k的意义是每个点上的数,以及询问中的数值z都 < 2 k <2^k <2k。之后n行,每行两个数x,y表示该点的位运算编号以及数值。
之后n - 1行,每行两个数x,y表示x和y之间有边相连。
之后m行,每行四个数,Q,x,y,z表示这次操作为Q(1位询问,2为更改),x,y,z意义如题所述。
输出格式:
对于每个操作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,m <= 1
对于另外20%的数据,k <= 5
对于另外20%的数据,位运算只会出现一种
对于100%的数据,0 <= n , m <= 100000 , k <= 64
题解
蒟蒻我一开始以为位运算什么的顺序不会有影响,样例都没玩就开始打, W A \mathcal{WA} WA成傻逼。。。
所以说,这道题最妙的地方就是, L C T \mathcal{LCT} LCT里维护的东西是对顺序有要求的,不同于和或者最大值是无序的。所以我们需要维护 S p l a y \mathcal{Splay} Splay里全 0 0 0串和全 1 1 1串中序遍历做完所有操作的结果的结果(为啥维护这个东西详见起床困难综合征)。
那么如何合并两个结果呢???
其实很简单,我们只需要把前面跑完后的是 0 0 0的地方跟后面的全 0 0 0串跑完的结果合并;同理,将 1 1 1的地方跟后面的全 1 1 1串合并。
全 0 0 0串的合并与全 1 1 1都是差不多的。
给出代码,配合理解:
sd merge(sd a,sd b)
{return (sd){(~a.s0&b.s0)|(a.s0&b.s1),(~a.s1&b.s0)|(a.s1&b.s1)};}
其他的就跟普通 L C T \mathcal{LCT} LCT与起床困难综合征大同小异了,看代码即可。
代码
#include<bits/stdc++.h>
#define inf ULONG_LONG_MAX
#define ls son[v][0]
#define rs son[v][1]
#define ll unsigned long long
using namespace std;
const int M=1e5+5;
struct sd{ll s0,s1;}val[M],ab[M],ba[M];
sd merge(sd a,sd b){return (sd){(~a.s0&b.s0)|(a.s0&b.s1),(~a.s1&b.s0)|(a.s1&b.s1)};}
int son[M][2],dad[M],n,m,k;
bool rev[M];
void swap(sd &a,sd &b){sd c=a;a=b;b=c;}
bool notroot(int v){return son[dad[v]][0]==v||son[dad[v]][1]==v;}
void up(int v)
{
ab[v]=ba[v]=val[v];
if(ls)ab[v]=merge(ab[ls],ab[v]),ba[v]=merge(ba[v],ba[ls]);
if(rs)ab[v]=merge(ab[v],ab[rs]),ba[v]=merge(ba[rs],ba[v]);
}
void turn(int v){swap(ab[v],ba[v]);swap(ls,rs);rev[v]^=1;}
void down(int v){if(rev[v]){if(ls)turn(ls);if(rs)turn(rs);rev[v]=0;}}
void push(int v){if(notroot(v))push(dad[v]);down(v);}
void spin(int v)
{
int f=dad[v],ff=dad[f],k=son[f][1]==v,w=son[v][!k];
if(notroot(f))son[ff][son[ff][1]==f]=v;son[v][!k]=f;son[f][k]=w;
if(w)dad[w]=f;dad[f]=v;dad[v]=ff;
up(f);
}
void splay(int v)
{
push(v);int f,ff;
while(notroot(v))
{
f=dad[v],ff=dad[f];
if(notroot(f))spin((son[f][0]==v)^(son[ff][0]==f)?v:f);
spin(v);
}
up(v);
}
void access(int v){for(int f=0;v;v=dad[f=v])splay(v),rs=f,up(v);}
void beroot(int v){access(v);splay(v);turn(v);}
void get(ll v,ll a,ll b)
{
switch(a)
{
case 1:val[v]=(sd){0ull,b};break;
case 2:val[v]=(sd){b,inf};break;
case 3:val[v]=(sd){b,~b};
}
}
void in()
{
ll a,b;
scanf("%d%d%d",&n,&m,&k);--k;
for(int i=1;i<=n;++i){scanf("%llu%llu",&a,&b);get(i,a,b);}
for(int i=1;i<n;++i){scanf("%llu%llu",&a,&b);beroot(a);dad[a]=b;}
}
ll work(ll a,ll b,ll c)
{
beroot(a);access(b);splay(b);
ll p0=ab[b].s0,p1=ab[b].s1,p,ans=0ull,w=0ull;
for(int i=k;i>=0;--i)
{
p=1ull<<i;
if(p0&p)ans+=p;
else if((c>=p)&&(p1&p))ans+=p,c-=p;
}
return ans;
}
void ac()
{
ll op,a,b,c;
for(int i=1;i<=m;++i)
{
scanf("%llu%llu%llu%llu",&op,&a,&b,&c);
if(op==1)printf("%llu\n",work(a,b,c));
else splay(a),get(a,b,c),up(a);
}
}
int main(){in();ac();}