【SDOI2017】樹點染色【線段樹+LCT】
阿新 • • 發佈:2018-10-29
get d+ i++ read 改變 rotate def 維護 n) ,把\(y\)接到\(x\)的右兒子
本來只是想練練LCT,沒想到是個線段樹
對於操作1:誒新的顏色?這不是access嗎?
也就是說,我們用一棵splay來表示一種顏色
操作2直接在LCT上亂搞……
不對啊,操作3要查子樹
誒好像是靜態的
那可以考慮線段樹維護dfs序
現在要考慮怎麽維護權值
我們發現開始的時候權值就是節點的深度
而在且只在access的時候會改變權值
試試魔改access?
原來:
for (int y=0;x;y=x,x=fa[x])
{
splay(x);
ch[x][1]=y;
update(x);
}
那麽主要就是\(ch[x][1]=y\)
實際上這句話包含兩個操作:清除\(ch[x][1]\)
清除\(ch[x][1]\),相當於把原來的重邊斷開
我們只考慮當前這個點改了之後的影響
註意既然他們是重邊連起來的,他們原來一定是同色的
而改了之後他們就不同了,所以y的子樹權值都會增加1
並且他也不會和這條到根路徑上任何一個出現過的顏色相同
同樣,把\(y\)接到\(x\)的右兒子,意味著x和y同色了,而原來是不同的,所以y的子樹權值都會減少1
線段樹維護一下就好
操作2直接單點查詢,類似樹上前綴和就可以了
於是乎,LCT40多行……
#include <iostream> #include <cstdio> #include <cstring> #include <cctype> #define MAXN 1000005 #define MAXM 2000005 using namespace std; inline int read() { int ans=0; char c=getchar(); while (!isdigit(c)) c=getchar(); while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar(); return ans; } struct edge { int u,v; }e[MAXM]; int head[MAXN],nxt[MAXM],cnt; void addnode(int u,int v) { e[++cnt]=(edge){u,v}; nxt[cnt]=head[u]; head[u]=cnt; } int dep[MAXN],fa[MAXN],up[MAXN][20]; int dfn[MAXN],pos[MAXN],tim,end[MAXN]; void dfs(int u) { dfn[u]=++tim; pos[tim]=u; for (int i=1;i<20;i++) up[u][i]=up[up[u][i-1]][i-1]; for (int i=head[u];i;i=nxt[i]) if (!dep[e[i].v]) { dep[e[i].v]=dep[u]+1; up[e[i].v][0]=u; fa[e[i].v]=u; dfs(e[i].v); } end[u]=tim; } int lca(int x,int y) { if (dep[x]<dep[y]) swap(x,y); int t=dep[x]-dep[y]; for (int i=0;(1<<i)<=t;i++) if (t&(1<<i)) x=up[x][i]; if (x==y) return x; for (int i=19;i>=0;i--) if (up[x][i]!=up[y][i]) x=up[x][i],y=up[y][i]; return up[x][0]; } namespace SGT { #define lc p<<1 #define rc p<<1|1 struct SegmentTree { int l,r; int lazy; int mx; }t[MAXN<<2]; void pushup(int p){t[p].mx=max(t[lc].mx,t[rc].mx);} void pushlazy(int p,int v){t[p].mx+=v,t[p].lazy+=v;} void pushdown(int p) { if (t[p].lazy) { pushlazy(lc,t[p].lazy); pushlazy(rc,t[p].lazy); t[p].lazy=0; } } void build(int p,int l,int r) { t[p].l=l,t[p].r=r; if (l==r){t[p].mx=dep[pos[l]];return;} int mid=(l+r)>>1; build(lc,l,mid),build(rc,mid+1,r); pushup(p); } void modify(int p,int l,int r,int v) { if (l<=t[p].l&&t[p].r<=r) return pushlazy(p,v); if (r<t[p].l||t[p].r<l) return; pushdown(p); if (l<=t[lc].r) modify(lc,l,r,v); if (t[rc].l<=r) modify(rc,l,r,v); pushup(p); } int querymax(int p,int l,int r) { pushdown(p); if (l<=t[p].l&&t[p].r<=r) return t[p].mx; if (r<t[p].l||t[p].r<l) return 0; int ans=0; if (l<=t[lc].r) ans=max(ans,querymax(lc,l,r)); if (t[rc].l<=r) ans=max(ans,querymax(rc,l,r)); return ans; } int query(int p,int k) { pushdown(p); if (t[p].l==t[p].r) return t[p].mx; if (k<=t[lc].r) return query(lc,k); else return query(rc,k); } } using namespace SGT; namespace Splay { int ch[MAXN][2]; bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;} int get(int x){return ch[fa[x]][1]==x;} void rotate(int x) { int y=fa[x],z=fa[y]; int l=get(x),r=l^1; int w=ch[x][r]; if (!isroot(y)) ch[z][get(y)]=x; ch[x][r]=y,ch[y][l]=w; if (w) fa[w]=y; fa[y]=x,fa[x]=z; } void splay(int x) { while (!isroot(x)) { int y=fa[x]; if (!isroot(y)) { if (get(x)==get(y)) rotate(y); else rotate(x); } rotate(x); } } } using namespace Splay; namespace LCT { int findroot(int x){while (ch[x][0]) x=ch[x][0];return x;} void access(int x) { int w; for (int y=0;x;y=x,x=fa[x]) { splay(x); if (ch[x][1]){w=findroot(ch[x][1]),modify(1,dfn[w],end[w],1);} if (ch[x][1]=y){w=findroot(y),modify(1,dfn[w],end[w],-1);} } } } using namespace LCT; int main() { int n,m; n=read(),m=read(); for (int i=1;i<n;i++) { int u,v; u=read(),v=read(); addnode(u,v),addnode(v,u); } dep[1]=1; dfs(1); build(1,1,n); while (m--) { int op,x,y,l; op=read(),x=read(); int ans; switch(op) { case 1:access(x);break; case 2: y=read(); l=lca(x,y); ans=query(1,dfn[x]); ans+=query(1,dfn[y]); ans-=(query(1,dfn[l])<<1); printf("%d\n",ans+1); break; case 3: printf("%d\n",querymax(1,dfn[x],end[x])); } } return 0; }
【SDOI2017】樹點染色【線段樹+LCT】