1. 程式人生 > >Luogu3613 睡覺困難綜合征

Luogu3613 睡覺困難綜合征

problem blog lct body access down 註意 都是 原來

題面描述https://www.luogu.org/problemnew/show/3613
原題NOI2014起床困難綜合癥https://www.luogu.org/problemnew/show/2114做完原題就可以嘗試解決這道題目了

題意:一棵n個節點的樹,每個節點上有一個位運算操作符(與、或、異或)和一個數字,走過一個點的時候當前值就會和這個數字做相應運算。現在給定x,y,z,要求在[0,z]中選區一個初值使從x點走到y點的最終結果最大。支持動態修改點上的操作符與數字。

我太菜了完全不知道樹鏈剖分去維護什麽只知道LCT一頓亂搞。。。
咳咳,首先原題[起床困難綜合癥]得要切掉,然後你就知道這題的大致做法了了:記錄初始以全0和全1走完後得到的答案,然後按位貪心即可(選高位一定比選低位優,就算低位全都可以選,因為這是二進制呀)。

為了方便起見,下文中所有的“答案”指以全0全1走完後得到的兩個值,以0為下標表示是以全0開始的,1為下標表示是以全1開始的。

LCT維護啥?

LCT維護的東西是:在以這個點為根的splay中(我沒有說這個點就是splay的根,是指以這個點為根的子樹),從前往後經過每一個點的答案以及從後往前走經過每一個點的答案。
比如說,有一個叫做1號節點的點,在以她為根的splay中還有點2,3,其中2是她的左兒子,3是她的右兒子,那麽在1號點上維護的東西就應該是:依次經過2-1-3的最終結果,以及依次經過3-1-2的最終結果。

那麽兩個結果怎麽合並呢?

假如說我們有兩段帶合並的已經計算出答案的區間,分別對應f0,f1和g0,g1。我們設合並後的答案是h0,h1,那麽有如下式子:

\[h0=(~f0&g0)+(f0&g1)\]
\[h1=(~f1&g0)+(f1&g1)\]
原因很簡單,請自己YY~

需要註意的點:

以往的LCT維護的都是子樹信息(廢話,這題不也是),但同時也是無序的。這題維護的子樹信息和左右子樹的循序是有關系的,所以原來的翻轉操作就需要略為修改。具體詳見代碼。

code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll unsigned long long
const int
N = 100005; struct data { ll f0,f1; data operator + (const data &b) const { data ans; ans.f0=(~f0&b.f0)|(f0&b.f1); ans.f1=(~f1&b.f0)|(f1&b.f1); return ans; } }f[N],lo[N],ro[N]; int n,m,fa[N],ls[N],rs[N],rev[N],Stack[N],top; ll gi() { ll x=0,w=1;char ch=getchar(); while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar(); if (ch=='-') w=0,ch=getchar(); while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); return w?x:-x; } bool isroot(int x){return ls[fa[x]]!=x&&rs[fa[x]]!=x;} void reverse(int x){swap(ls[x],rs[x]);swap(lo[x],ro[x]);rev[x]^=1;} void pushdown(int x){if (rev[x]) reverse(ls[x]),reverse(rs[x]),rev[x]=0;} void pushup(int x) { lo[x]=ro[x]=f[x]; if (ls[x]) lo[x]=lo[ls[x]]+lo[x],ro[x]=ro[x]+ro[ls[x]]; if (rs[x]) lo[x]=lo[x]+lo[rs[x]],ro[x]=ro[rs[x]]+ro[x]; } void R_rotate(int x) { int y=fa[x],z=fa[y]; ls[y]=rs[x]; if (rs[x]) fa[rs[x]]=y; fa[x]=z; if (!isroot(y)) if (y==ls[z]) ls[z]=x;else rs[z]=x; rs[x]=y;fa[y]=x; pushup(y); } void L_rotate(int x) { int y=fa[x],z=fa[y]; rs[y]=ls[x]; if (ls[x]) fa[ls[x]]=y; fa[x]=z; if (!isroot(y)) if (y==ls[z]) ls[z]=x;else rs[z]=x; ls[x]=y;fa[y]=x; pushup(y); } void splay(int x) { Stack[top=1]=x; for (int i=x;!isroot(i);i=fa[i]) Stack[++top]=fa[i]; while (top) pushdown(Stack[top--]); while (!isroot(x)) { int y=fa[x],z=fa[y]; if (isroot(y)) if (x==ls[y]) R_rotate(x); else L_rotate(x); else if (y==ls[z]) if (x==ls[y]) R_rotate(y),R_rotate(x); else L_rotate(x),R_rotate(x); else if (x==ls[y]) R_rotate(x),L_rotate(x); else L_rotate(y),L_rotate(x); } pushup(x); } void access(int x){for (int y=0;x;y=x,x=fa[x]) splay(x),rs[x]=y,pushup(x);} void makeroot(int x){access(x);splay(x);reverse(x);} void split(int x,int y){makeroot(x);access(y);splay(y);} void link(int x,int y){makeroot(x);fa[x]=y;} int main() { n=gi();m=gi();gi(); for (int i=1;i<=n;i++) { int x=gi();ll y=gi(); if (x==1) f[i]=(data){0,y}; if (x==2) f[i]=(data){y,~0}; if (x==3) f[i]=(data){y,~y}; } for (int i=1;i<n;i++) { int x=gi(),y=gi(); link(x,y); } while (m--) { int opt=gi(),x=gi(),y=gi();ll z=gi(); if (opt==1) { split(x,y);ll e=1,ans=0; for (int k=63;k>=0;k--) if (lo[y].f0&(e<<k)) ans+=e<<k; else if (lo[y].f1&(e<<k)&&z>=(e<<k)) z-=e<<k,ans+=e<<k; printf("%llu\n",ans); } if (opt==2) { makeroot(x); if (y==1) f[x]=(data){0,z}; if (y==2) f[x]=(data){z,~0}; if (y==3) f[x]=(data){z,~z}; pushup(x); } } return 0; }

Luogu3613 睡覺困難綜合征