[Luogu 3613] 睡覺困難綜合徵
Description
給定一棵 \(n\) 個點的樹,每個點上有位運算 \(opt\) 和一個權值 \(x\),位運算有 &,|,^ 三種。
要求支援:
- 修改點 \(v\) 的 \(opt\) 和 \(x_v\)
- 確定一個初始點權在 \([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\)
那有了這個能不能快速更新呢?也就是說有左區間的 \(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\)
最後求出來 從 \(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;}