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\n",ans);
106         }
107         else x=read(),modify(x);
108     }
109     return 0;
110 }

 

posted @ 2018-08-05 19:17  bztMinamoto  阅读(747)  评论(0编辑  收藏  举报
Live2D