SP2666 QTREE4 - Query on a tree IV(LCT)
Posted bztminamoto
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SP2666 QTREE4 - Query on a tree IV(LCT)相关的知识,希望对你有一定的参考价值。
题意翻译
你被给定一棵n个点的带边权的树(边权可以为负),点从1到n编号。每个点可能有两种颜色:黑或白。我们定义dist(a,b)为点a至点b路径上的权值之和。
一开始所有的点都是白色的。
要求作以下操作:
C a 将点a的颜色反转(黑变白,白变黑)
A 询问dist(a,b)的最大值。a,b点都必须为白色(a与b可以相同),显然如果树上仍存在白点,查询得到的值一定是个非负数。
特别地,如果作‘A‘操作时树上没有白点,输出"They have disappeared."。
题解
一道神仙题……完全不知道这题大佬们是怎么想出思路的orz这里
然而还是看不太懂决定自己写一下……
本来打算用捉迷藏一样做的……但发现我那一道写的时候是用括号表示做的……这题有边权不能做……
说下思路吧
LCT只能维护点权,但本题中是边权
我们可以指定一个根(比如1),然后令所有的边权变成它指向的儿子的点权,记为$len$
ps:这样做之后会有很多细节,需要一一注意
然后在考虑一下如何求出两个相邻最远的白点的距离呢?
因为LCT上面有实边和虚边,所以实儿子和虚儿子的信息要用不同的方法维护
实边构成了一个splay,我们定义几个数组,$lmx$表示splay中深度最浅的点能够到达的最远的白点的距离,$rmx$表示splay中深度最深的点能够到达的最远的白点的距离,$mxs$表示splay中距离最远的两个白点的距离,也就是答案,$sum$表示整棵splay的长度和
ps:LCT中的splay在原树中是一条链!所以$sum$维护的是整条链的长度和,同理$lmx$表示这条链的顶端距离白点的最远距离,$rmx$表示这条链的底端距离白点的最远距离
虚儿子的信息怎么维护?虚儿子我们需要记录的只有到白点的最远距离,以及虚儿子中的$mxs$,直接开两个$set$丢进去就好了
那么,这些信息够了吗?
当然已经够了,因为已经足够将信息不断向上传递了
先考虑一下虚子树对$mxs$的影响,实边太复杂了待会儿再说
因为在$access$的时候,会有换边的操作
对于由实变虚的原右儿子,把$mx[rs]$丢进路径的$set$里,把$lx[rs]$丢进链的$set$里
同理,把由虚变实的新右儿子$mx[y]$和$lx[y]$从对应的$set$里删除就行
$mx$丢进去我们可以理解,因为要维护答案,但为什么链的长度只要把$lx$丢进$set$就行了呢?
考虑一下,$set$里维护的是这一个顶点到其虚子树中的白点的最大距离,而$lx[rs]$代表的是$x$的右子树中深度最浅的点到白点的最大距离,如果回到原树上,这就是$x$的儿子到白点的最大距离!!又因为我们将边权给了儿子,所以,只需要记录虚边的$ls$即可
然后期待已久(写到吐血)pushup操作了
这部分还是具体看代码好了……细节太多了……我这里简单提几嘴
$lmx$要过子树的最低点,所以等于$max(lmx[ls],max(虚链中最长+整个左子树,右子树中最长+整个左子树))$
$rmx$同理
然后$mxs$就是把所有可能的链给接起来以及子树的答案都弄出来更新就好了
ps:如果$x$也是白点,他自己也得拿出来更新$mxs$
时间复杂度$O(n log^2 n)$
然而如果$set$的$size$大说明虚边多,深度不大,LCT操作次数少一点
如果树的深度深,$set$的$size$又不会太大,所以时间复杂度不是很严格
ps:请务必详细看看注解,这题细节多的要命
1 //minamoto 2 #include<bits/stdc++.h> 3 using namespace std; 4 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) 5 char buf[1<<21],*p1=buf,*p2=buf; 6 template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;} 7 inline int read(){ 8 #define num ch-‘0‘ 9 char ch;bool flag=0;int res; 10 while((ch=getc())<‘0‘||ch>‘9‘) 11 (ch==‘-‘)&&(flag=true); 12 for(res=num;(ch=getc())>=‘0‘&&ch<=‘9‘;res=res*10+num); 13 (flag)&&(res=-res); 14 #undef num 15 return res; 16 } 17 const int N=200005,inf=0x3f3f3f3f; 18 int n,ver[N],Next[N],head[N],edge[N],tot,ans=-inf,col[N]; 19 inline void add(int u,int v,int e){ver[++tot]=v,Next[tot]=head[u],head[u]=tot,edge[tot]=e;} 20 inline int fir(multiset<int> &s){return s.size()?*s.rbegin():-inf;} 21 inline int sec(multiset<int> &s){return s.size()>1?*(++s.rbegin()):-inf;} 22 /*分别是查询set里的最大和次大*/ 23 #define ls ch[x][0] 24 #define rs ch[x][1] 25 int ch[N][2],fa[N],lmx[N],rmx[N],mxs[N],sum[N],len[N],w[N]; 26 /*w表示这个点是否是白点,如果是赋值为0,否则赋值为-inf*/ 27 multiset<int> chain[N],path[N]; 28 /*chain存链长,path存路径长*/ 29 inline bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;} 30 void init(){for(int i=0;i<=n;++i) lmx[i]=rmx[i]=mxs[i]=-inf;} 31 void pushup(int x){ 32 sum[x]=sum[ls]+sum[rs]+len[x]; 33 /*sum表示整棵splay的长度和*/ 34 int cha=max(w[x],fir(chain[x])); 35 /*找最远的虚边上的白点的距离*/ 36 int L=max(cha,rmx[ls]+len[x]); 37 /*向左子树或向虚边最远能走多远 38 ps:这里len要加上去,因为左子树在原树中是x的祖先 39 要加上x到父亲的距离(已经被转化为点权了)*/ 40 int R=max(cha,lmx[rs]); 41 /*向右子树或向虚边最远能走多远*/ 42 lmx[x]=max(lmx[ls],sum[ls]+len[x]+R); 43 /*不经过x,或经过x并走到最远 44 注意左子树在原树中是一条链!不存在路径重叠问题*/ 45 rmx[x]=max(rmx[rs],sum[rs]+L); 46 /*同理*/ 47 mxs[x]=max(rmx[ls]+len[x]+R,lmx[rs]+L); 48 cmax(mxs[x],max(mxs[ls],mxs[rs])); 49 cmax(mxs[x],fir(path[x])); 50 cmax(mxs[x],fir(chain[x])+sec(chain[x])); 51 /*最长链和次长链可以形成一条路径*/ 52 if(w[x]==0) cmax(mxs[x],max(fir(chain[x]),0)); 53 /*用一堆东西来更新答案*/ 54 } 55 void rotate(int x){ 56 int y=fa[x],z=fa[y],d=ch[y][1]==x; 57 if(!isroot(y)) ch[z][ch[z][1]==y]=x; 58 fa[x]=z,fa[y]=x,fa[ch[x][d^1]]=y,ch[y][d]=ch[x][d^1],ch[x][d^1]=y,pushup(y); 59 } 60 void splay(int x){ 61 for(int y=fa[x],z=fa[y];!isroot(x);y=fa[x],z=fa[y]){ 62 if(!isroot(y)) 63 ((ch[y][1]==x)^(ch[z][1]==y))?rotate(x):rotate(y); 64 rotate(x); 65 } 66 pushup(x); 67 } 68 void access(int x){ 69 for(int y=0;x;x=fa[y=x]){ 70 splay(x); 71 if(rs) path[x].insert(mxs[rs]),chain[x].insert(lmx[rs]); 72 if(y) path[x].erase(path[x].find(mxs[y])),chain[x].erase(chain[x].find(lmx[y])); 73 rs=y,pushup(x); 74 /*注意虚实子树变换时要更新path和chain*/ 75 } 76 } 77 void modify(int x){ 78 /*改变点的颜色,col为1表示黑,0表示白*/ 79 access(x),splay(x); 80 col[x]^=1,w[x]=col[x]?(-inf):0; 81 pushup(x),ans=mxs[x]; 82 } 83 void dfs(int u){ 84 for(int i=head[u];i;i=Next[i]){ 85 int v=ver[i]; 86 if(v==fa[u]) continue; 87 fa[v]=u,len[v]=edge[i],dfs(v); 88 /*把边的权值给儿子*/ 89 chain[u].insert(lmx[v]),path[u].insert(mxs[v]); 90 } 91 pushup(u); 92 } 93 int main(){ 94 //freopen("testdata.in","r",stdin); 95 n=read();init(); 96 for(int i=1;i<n;++i){ 97 int u=read(),v=read(),e=read(); 98 add(u,v,e),add(v,u,e); 99 } 100 dfs(1),ans=mxs[1];int q=read(); 101 while(q--){ 102 char op=getc();int x; 103 getc(); 104 if(op==‘A‘){ 105 ans<0?puts("They have disappeared."):printf("%d ",ans); 106 } 107 else x=read(),modify(x); 108 } 109 return 0; 110 }
以上是关于SP2666 QTREE4 - Query on a tree IV(LCT)的主要内容,如果未能解决你的问题,请参考以下文章
[SPOJ2666][ZJOI2007]捉迷藏Query on a tree IV(树链剖分)(论文做法)
题解 SP375 QTREE - Query on a tree