• 洛谷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++='
    ';
    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 }
  • 相关阅读:
    五、Docker网络管理
    四、Docker存储管理
    三、Docker镜像基础管理
    二、Docker容器基础管理
    模仿DotnetCore中间件的方式,做一个列表过滤的功能
    web开发常见的鉴权方式
    Go语言使用Godep进行包管理
    Go语言中的数据格式(json、xml 、msgpack、protobuf)
    epoll原理解释(转)
    内存管理之内存池概述(转)
  • 原文地址:https://www.cnblogs.com/bztMinamoto/p/9430957.html
Copyright © 2020-2023  润新知