洛谷P3613 睡觉困难综合征

传送门

题解

人生第一道由乃……

做这题之前应该先去把这一题给切掉->这里

我的题解->这里

然后先膜一波zsy大佬和flashhu大佬

大体思路就是先吧全0和全1的都跑答案,然后按位贪心

我一开始想到的是每一次split,然后直接一个一个跑

后来发现时间复杂度肯定爆炸……

看了看网上其他的,发现说的都不怎么清楚……结果硬是理解了好久才明白……

先考虑一下LCT维护什么

定义$f0$为全0走过一条路径之后的答案,$f1$表示全1走过一条路径之后的答案

LCT需要维护的是splay中以x为根的子树里,从前往后遍历(即中序遍历)的$f0$和$f1$以及从后往前(即与前面完全相反的顺序)的$f0$和$f1$

比如有一棵splay,x是其中一个点,它在splay中有两个儿子,左儿子y和右儿子z,那么从前往后遍历就是路径y->x->z,从后往前就是路径z->x->y

然后思考,如果已经有了两个区间,该如何合并他们

假如说我们有两段计算出答案的区间,分别对应f0,f1和g0,g1。我们设合并后的答案是h0,h1,那么有如下式子:

$h0=(~f0&g0)+(f0&g1)$

$h1=(~f1&g0)+(f1&g1)$

为啥?

全0走到最后的话,先考虑两种情况

全0走到中间等于1的那几位,走后一半的答案等同于全1走后一半的这几位的答案

全0走到中间等于0的那几位,走后一半的答案等同于全0走后一半的这几位的答案

只需要把这两个答案加起来即可

(ps:这里默认f为前一半的答案,g为后一半的答案)

全1走到最后同理

然后就是几个细节

1.pushdown的时候因为有翻转标记,从前往后走和从后往前走的答案也要交换

2.按位贪心用1做位运算的时候,记得把1设成unsigned long long(简单来说就是设一个ull变量让它等于1)(我就是因为一开始直接用1做位运算结果死都调不出来……)

  1 // luogu-judger-enable-o2
  2 //minamoto
  3 #include<iostream>
  4 #include<cstdio>
  5 #define ll unsigned long long
  6 using std::swap;
  7 using std::cout;
  8 using std::endl;
  9 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
 10 char buf[1<<21],*p1=buf,*p2=buf;
 11 inline ll read(){
 12     #define num ch-'0'
 13     char ch;bool flag=0;ll res;
 14     while(!isdigit(ch=getc()))
 15     (ch=='-')&&(flag=true);
 16     for(res=num;isdigit(ch=getc());res=res*10+num);
 17     (flag)&&(res=-res);
 18     #undef num
 19     return res;
 20 }
 21 char obuf[1<<24],*o=obuf;
 22 void print(ll x){
 23     if(x>9) print(x/10);
 24     *o++=x%10+48;
 25 }
 26 const int N=100005;
 27 struct node{
 28     ll f0,f1;
 29     inline node operator +(const node &b)const
 30     {
 31         node a;
 32         a.f0=(~f0&b.f0)|(f0&b.f1);
 33         a.f1=(~f1&b.f0)|(f1&b.f1);
 34         return a;
 35     }
 36 }f[N],l[N],r[N];
 37 int fa[N],ch[N][2],s[N],rev[N],top,tot;
 38 int ver[N<<1],head[N],Next[N<<1];
 39 inline void add(int u,int v){
 40     ver[++tot]=v,Next[tot]=head[u],head[u]=tot;
 41     ver[++tot]=u,Next[tot]=head[v],head[v]=tot;
 42 }
 43 inline bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
 44 #define ls ch[x][0]
 45 #define rs ch[x][1]
 46 inline void pushup(int x){
 47     l[x]=r[x]=f[x];
 48     if(ls) l[x]=l[ls]+l[x],r[x]=r[x]+r[ls];
 49     if(rs) l[x]=l[x]+l[rs],r[x]=r[rs]+r[x];
 50 }
 51 inline void pushr(int x){
 52     swap(ls,rs),swap(l[x],r[x]),rev[x]^=1;
 53 }
 54 inline void pushdown(int x){
 55     if(rev[x]&&x){
 56         pushr(ls),pushr(rs),rev[x]=0;
 57     }
 58 }
 59 void rotate(int x){
 60     int y=fa[x],z=fa[y],d=ch[y][1]==x;
 61     if(!isroot(y)) ch[z][ch[z][1]==y]=x;
 62     fa[x]=z,fa[y]=x,fa[ch[x][d^1]]=y,ch[y][d]=ch[x][d^1],ch[x][d^1]=y,pushup(y);
 63 }
 64 void splay(int x){
 65     s[top=1]=x;for(int i=x;!isroot(i);i=fa[i]) s[++top]=fa[i];
 66     while(top) pushdown(s[top--]);
 67     for(int y=fa[x],z=fa[y];!isroot(x);y=fa[x],z=fa[y]){
 68         if(!isroot(y))
 69         ((ch[y][1]==x)^(ch[z][1]==y))?rotate(x):rotate(y);
 70         rotate(x);
 71     }
 72     pushup(x);
 73 }
 74 void access(int x){
 75     for(int y=0;x;x=fa[y=x]){
 76         splay(x),rs=y,pushup(x);
 77     }
 78 }
 79 void makeroot(int x){
 80     access(x),splay(x),pushr(x);
 81 }
 82 void split(int x,int y){
 83     makeroot(x),access(y),splay(y);
 84 }
 85 void dfs(int u){
 86     for(int i=head[u];i;i=Next[i]){
 87         int v=ver[i];
 88         if(v==fa[u]) continue;
 89         fa[v]=u,dfs(v);
 90     }
 91     pushup(u);
 92 }
 93 int main(){
 94     //freopen("testdata.in","r",stdin);
 95     int n=read(),m=read(),k=read();
 96     for(int i=1;i<=n;++i){
 97         int x=read();ll y=read();
 98         switch(x){
 99             case 1:f[i]=(node){0,y};break;
100             case 2:f[i]=(node){y,~0};break;
101             case 3:f[i]=(node){y,~y};break;
102         }
103     }
104     for(int i=1;i<n;++i){
105         int u=read(),v=read();add(u,v);
106     }
107     dfs(1);
108     while(m--){
109         int opt=read(),x=read(),y=read();ll z=read();
110         if(opt&1){
111             split(x,y);ll ans=0,e=1;
112             for(int i=k-1;i>=0;--i){
113                 if(l[y].f0&(e<<i)) ans|=e<<i;
114                 else if((l[y].f1&(e<<i))&&z>=(e<<i)) ans|=e<<i,z^=e<<i;
115             }
116             print(ans),*o++='\n';
117         }
118         else{
119             switch(y){
120                 case 1:f[x]=(node){0,z};break;
121                 case 2:f[x]=(node){z,~0};break;
122                 case 3:f[x]=(node){z,~z};break;
123             }
124             splay(x);
125         }
126     }
127     fwrite(obuf,o-obuf,1,stdout);
128     return 0;
129 }

 

转载于:https://www.cnblogs.com/bztMinamoto/p/9430957.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值