1. 程式人生 > >[Luogu 3613] 睡覺困難綜合徵

[Luogu 3613] 睡覺困難綜合徵

Description

給定一棵 \(n\) 個點的樹,每個點上有位運算 \(opt\) 和一個權值 \(x\),位運算有 &,|,^ 三種。

要求支援:

  1. 修改點 \(v\)\(opt\)\(x_v\)
  2. 確定一個初始點權在 \([0,z]\) 之間的 \(v_0\),然後依次經過從 \(x\)\(y\) 的所有節點。每經過一個節點 \(i\)\(v\) 就變成 \(v\;opt\;x_i\) 請回答最後到 \(y\) 時可能的最大的 \(v\)

Solution

首先五十分的做法比較顯然,就是拆位建 \(lct\),然後每位分開做,複雜度 \(O(nk\log n)\)

這樣只有 \(50\) 分,考慮優化複雜度。

因為 \(O(n\log n)\) 已經是 \(lct\) 的複雜度了不是很能優化掉,考慮去掉複雜度乘上的 \(k\)

有這個 \(k\) 的原因是我們對於每一位都搞了棵 \(lct\) 出來。如果我們只弄一棵 \(lct\) 是否可行呢?

定義 \(f0,f1\) 分別為初始值全 \(0/1\) 走過一條路徑之後的答案

那在 \(lct\) 中需要維護的就是 \(splay\)\(x\) 的子樹,中序遍歷的 \(f0,f1\) 已經倒著的中序遍歷(與中序遍歷相反)\(f0,f1\) (要維護這個倒著的原因是當前點 \(x\) 的值與左右子樹的順序有關,而 \(splay\)

中又有 \(reverse\) 操作所以要維護)。

那有了這個能不能快速更新呢?也就是說有左區間的 \(f0,f1\) 和右區間的 \(g0,g1\),能不能快速求出來 \(h0,h1\) 呢?廢話

更新式子比較顯然就是:\(h0=(f0\&g1)+(\sim f0\& g0),h1=(f1\And g1)+(\sim f1\& g0)\)

證明的話就是全 \(0\) 放進去左區間之後跑出來的是 \(f0\),可能長 \(10010001010\) 這樣,然後再去 $\And $ 一下 \(g1\) 就代表這些當前為 \(1\) 的位放進右區間之後跑出來的是多少。因為 \(\&\)\(f0\)

所以最後答案肯定不會比 \(f0\) 大。其它幾項的證明也類似就不去證了。

最後求出來 從 \(x\)\(y\)\(f0,f1\) 之後貪心的選就好了。

Code

#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<cctype>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using std::min;
using std::max;
using std::swap;
using std::vector;
typedef double db;
typedef unsigned long long ll;
#define pb(A) push_back(A)
#define pii std::pair<int,int>
#define all(A) A.begin(),A.end()
#define mp(A,B) std::make_pair(A,B)

namespace NewweN{
    
    const int N=1e5+5;

    int n,m,cnt,head[N],stk[N],top;
    int fa[N],ch[N][2],tag[N],k;ll maxn;
    char buf[1048578];int ptr,MX;
    #define ls ch[x][0]
    #define rs ch[x][1]

    struct Edge{
        int to,nxt;
    }edge[N<<1];

    void add(int x,int y){
        edge[++cnt].to=y;
        edge[cnt].nxt=head[x];
        head[x]=cnt;
    }

    struct Node{
        ll a,b;
        Node(){}
        Node(ll x,ll y){
            a=x,b=y;
        }
        friend Node operator+(Node x,Node y){
            return Node((x.a&y.b)+(((~x.a))&y.a),(x.b&y.b)+(((~x.b))&y.a));
        }
    }l[N],r[N],val[N];
    
    char nc(){
        if(ptr==MX) MX=fread(buf,1,1<<20,stdin),ptr=0;
        return ptr==MX?EOF:buf[ptr++];
    }
    #define getchar nc
    ll getint(){
        ll X=0,w=0;char ch=getchar();
        while(!isdigit(ch))w|=ch=='-',ch=getchar();
        while( isdigit(ch))X=X*10+ch-48,ch=getchar();
        if(w) return -X;return X;
    }

    void dfs(int now,int f=0){
        for(int i=head[now];i;i=edge[i].nxt){
            int to=edge[i].to;
            if(to==f)continue;
            fa[to]=now;
            dfs(to,now);
        }
    }

    void pushup(int x){
        l[x]=r[x]=val[x];
        if(ls)l[x]=l[ls]+l[x],r[x]=r[x]+r[ls];
        if(rs)l[x]=l[x]+l[rs],r[x]=r[rs]+r[x];
    }

    void pushr(int x){
        tag[x]^=1;swap(ls,rs);swap(l[x],r[x]);
    }

    void pushdown(int x){
        if(tag[x])tag[x]=0,pushr(ls),pushr(rs);
    }

    bool nroot(int x){
        return ch[fa[x]][0]==x or ch[fa[x]][1]==x;
    }

    void rotate(int x){
        int y=fa[x],z=fa[y],d=ch[y][1]==x,dd=ch[z][1]==y;
        ch[y][d]=ch[x][d^1];if(ch[x][d^1])fa[ch[x][d^1]]=y;
        fa[x]=z;if(nroot(y))ch[z][dd]=x;
        fa[y]=x;ch[x][d^1]=y;pushup(y);
    }

    void splay(int x){
        int now=x;stk[++top]=now;
        while(nroot(now))now=fa[now],stk[++top]=now;
        while(top)pushdown(stk[top--]);
        while(nroot(x)){
            int y=fa[x],z=fa[y];
            if(nroot(y))rotate((ch[y][1]==x)^(ch[z][1]==y)?x:y);
            rotate(x);
        }pushup(x);
    }

    void access(int x){
        for(int y=0;x;y=x,x=fa[x]){
            splay(x);rs=y;
            pushup(x);
        }
    }

    void makeroot(int x){
        access(x),splay(x),pushr(x);
    }

    void split(int x,int y){
        makeroot(x),access(y),splay(y);
    }

    signed main(){
        n=getint(),m=getint(),k=getint();
        maxn=(1ull<<k)-1;
        for(int i=1;i<=n;i++){
            ll x=getint(),y=getint();
            if(x==1)val[i]=Node(0,y);
            if(x==2)val[i]=Node(y,~0);
            if(x==3)val[i]=Node(y,(~y));
        }
        for(int i=1;i<n;i++){
            int x=getint(),y=getint();
            add(x,y),add(y,x);
        } dfs(1);
        while(m--){
            int opt=getint(),x=getint(),y=getint();ll z=getint();
            if(opt==1){
                split(x,y);
                ll ans=0,used=0;
                for(int i=k-1;~i;i--){
                    if(l[y].a>>i&1ull) ans|=1ull<<i;
                    else if((l[y].b>>i&1ull) and (used|(1ull<<i))<=z) ans|=1ull<<i,used|=1ull<<i;
                } printf("%llu\n",ans);
            } else{
                splay(x);
                if(y==1) val[x]=Node(0,z);
                if(y==2) val[x]=Node(z,~0);
                if(y==3) val[x]=Node(z,(~z));
                pushup(x);
            }
        } return 0;
    }
}

int yzh=NewweN::main();

signed main(){return 0;}