BZOJ2325 [ZJOI2011]道馆之战 树链剖分 线段树
欢迎访问~原文出处——博客园-zhouzhendong
去博客园看该题解
题目传送门 - BZOJ2325
题意概括
给你一棵N个点的树,树上的每个节点有A,B两块区域,且每种区域有两种状态:可以走的“.”,不能走的“#”。每次只能移动到相邻节点的同一类区域(AA,BB)或这个房间的另一区域(AB,BA)。
现在有Q个操作,操作分两种:
C x s :将x节点A,B的区域的状态改为s
Q x y :询问从x出发走到y(不能往回走)最多可以走过几个区域(可以不走到y)。
N≤ 30 000 , Q ≤ 80 000
题解
对于这道题目,我们很容易就能想到树剖,那么链上的问题就变成序列上的单点修改+区间询问了。
而显然这道题目的信息是可以用线段树来维护的,我们只需要在每个节点上维护8个标记:从左上到右上,从左上到右下,从左下到右上,从左下到右下的最长路径以及从左上,从左下,从右上,从右下出发的最长路径就行了。这些标记都是可以O(1)合并的,所以总的时间复杂度就是 $O(Q\log^2n)$ 了。
写的时候因为炸int莫名其妙错了很久……
代码
#include <cstring> #include <algorithm> #include <cstdio> #include <cstdlib> #include <cmath> using namespace std; const int N=30005,Inf=1e7; int max(int a,int b,int c){return max(a,max(b,c));} int max(int a,int b,int c,int d){return max(a,max(b,c,d));} struct Gragh{ int cnt,y[N*2],nxt[N*2],fst[N]; void clear(){ cnt=0; memset(fst,0,sizeof fst); } void add(int a,int b){ y[++cnt]=b,nxt[cnt]=fst[a],fst[a]=cnt; } }g; int n,m; int fa[N],size[N],depth[N],son[N],top[N],p[N],ap[N],cnp=0; int A[N],B[N]; void Get_Gen_Info(int rt,int pre,int d){ fa[rt]=pre,size[rt]=1,depth[rt]=d,son[rt]=-1; for (int i=g.fst[rt];i;i=g.nxt[i]) if (g.y[i]!=pre){ int s=g.y[i]; Get_Gen_Info(s,rt,d+1); size[rt]+=size[s]; if (son[rt]==-1||size[s]>size[son[rt]]) son[rt]=s; } } void Get_Top(int rt,int tp){ top[rt]=tp; ap[p[rt]=++cnp]=rt; if (son[rt]==-1) return; Get_Top(son[rt],tp); for (int i=g.fst[rt];i;i=g.nxt[i]){ int s=g.y[i]; if (s!=fa[rt]&&s!=son[rt]) Get_Top(s,s); } } struct Tree{ int LA,RA,LB,RB; int AA,AB,BA,BB; Tree (){} Tree (int x){LA=RA=LB=RB=AA=AB=BA=BB=x;} bool empty(){return !(LA||RA||LB||RB||AA||AB||BA||BB);} void getnode(int a,int b){//1有路 0障碍 if (a&&b)LA=RA=LB=RB=AB=BA=2,AA=BB=1; if (a&&!b)LA=RA=AA=1,LB=RB=AB=BA=BB=-Inf; if (!a&&b)LB=RB=BB=1,LA=RA=AA=AB=BA=-Inf; if (!a&&!b)LA=RA=LB=RB=AA=AB=BA=BB=-Inf; } void rev(){ swap(LA,RA); swap(LB,RB); swap(AB,BA); } }t[N*4]; Tree operator + (Tree ls,Tree rs){ Tree rt; if (ls.empty())return rs; if (rs.empty())return ls; rt.LA=max(ls.LA,ls.AA+rs.LA,ls.AB+rs.LB,-Inf); rt.LB=max(ls.LB,ls.BA+rs.LA,ls.BB+rs.LB,-Inf); rt.RA=max(rs.RA,rs.AA+ls.RA,rs.BA+ls.RB,-Inf); rt.RB=max(rs.RB,rs.AB+ls.RA,rs.BB+ls.RB,-Inf); rt.AA=max(ls.AA+rs.AA,ls.AB+rs.BA,-Inf); rt.AB=max(ls.AA+rs.AB,ls.AB+rs.BB,-Inf); rt.BA=max(ls.BA+rs.AA,ls.BB+rs.BA,-Inf); rt.BB=max(ls.BA+rs.AB,ls.BB+rs.BB,-Inf); return rt; } void build(int rt,int L,int R){ if (L==R){ t[rt].getnode(A[ap[L]],B[ap[L]]); return; } int mid=(L+R)>>1,ls=rt<<1,rs=ls|1; build(ls,L,mid); build(rs,mid+1,R); t[rt]=t[ls]+t[rs]; } void change(int rt,int L,int R,int pos){ if (L==R){ t[rt].getnode(A[ap[L]],B[ap[L]]); return; } int mid=(L+R)>>1,ls=rt<<1,rs=ls|1; if (pos<=mid) change(ls,L,mid,pos); else change(rs,mid+1,R,pos); t[rt]=t[ls]+t[rs]; } Tree query(int rt,int L,int R,int xle,int xri){ if (R<xle||L>xri) return Tree(0); if (xle<=L&&R<=xri) return t[rt]; int mid=(L+R)>>1,ls=rt<<1,rs=ls|1; return query(ls,L,mid,xle,xri)+query(rs,mid+1,R,xle,xri); } int Tquery(int a,int b){ int f1=top[a],f2=top[b],swaped=0,res; Tree ans,ans1(0),ans2(0); while (f1!=f2){ if (depth[f1]<depth[f2]) swap(f1,f2),swap(a,b),swap(ans1,ans2),swaped^=1; ans1=query(1,1,n,p[f1],p[a])+ans1; a=fa[f1],f1=top[a]; } if (depth[a]<depth[b]) swap(a,b),swap(ans1,ans2),swaped^=1; ans1=query(1,1,n,p[b],p[a])+ans1; if (swaped) swap(a,b),swap(ans1,ans2); ans1.rev(); ans=ans1+ans2; res=max(ans.LA,ans.LB); return res>0?res:0; } int main(){ g.clear(); scanf("%d%d",&n,&m); for (int i=1,a,b;i<n;++i){ scanf("%d%d",&a,&b); g.add(a,b); g.add(b,a); } Get_Gen_Info(1,0,0); Get_Top(1,1); char s[3]; for (int i=1;i<=n;i++){ scanf("%s",s); A[i]=s[0]=='.'; B[i]=s[1]=='.'; } build(1,1,n); for (int i=1;i<=m;i++){ char op[3]; int x,st,en; scanf("%s",op); if (op[0]=='Q'){ scanf("%d%d",&st,&en); printf("%d\n",Tquery(st,en)); } else { scanf("%d%s",&x,s); A[x]=s[0]=='.'; B[x]=s[1]=='.'; change(1,1,n,p[x]); } } return 0; }