题意翻译

你被给定一棵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:请务必详细看看注解,这题细节多的要命

 //minamoto
#include<bits/stdc++.h>
using namespace std;
#define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[<<],*p1=buf,*p2=buf;
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,:;}
inline int read(){
#define num ch-'0'
char ch;bool flag=;int res;
while((ch=getc())<''||ch>'')
(ch=='-')&&(flag=true);
for(res=num;(ch=getc())>=''&&ch<='';res=res*+num);
(flag)&&(res=-res);
#undef num
return res;
}
const int N=,inf=0x3f3f3f3f;
int n,ver[N],Next[N],head[N],edge[N],tot,ans=-inf,col[N];
inline void add(int u,int v,int e){ver[++tot]=v,Next[tot]=head[u],head[u]=tot,edge[tot]=e;}
inline int fir(multiset<int> &s){return s.size()?*s.rbegin():-inf;}
inline int sec(multiset<int> &s){return s.size()>?*(++s.rbegin()):-inf;}
/*分别是查询set里的最大和次大*/
#define ls ch[x][0]
#define rs ch[x][1]
int ch[N][],fa[N],lmx[N],rmx[N],mxs[N],sum[N],len[N],w[N];
/*w表示这个点是否是白点,如果是赋值为0,否则赋值为-inf*/
multiset<int> chain[N],path[N];
/*chain存链长,path存路径长*/
inline bool isroot(int x){return ch[fa[x]][]!=x&&ch[fa[x]][]!=x;}
void init(){for(int i=;i<=n;++i) lmx[i]=rmx[i]=mxs[i]=-inf;}
void pushup(int x){
sum[x]=sum[ls]+sum[rs]+len[x];
/*sum表示整棵splay的长度和*/
int cha=max(w[x],fir(chain[x]));
/*找最远的虚边上的白点的距离*/
int L=max(cha,rmx[ls]+len[x]);
/*向左子树或向虚边最远能走多远
ps:这里len要加上去,因为左子树在原树中是x的祖先
要加上x到父亲的距离(已经被转化为点权了)*/
int R=max(cha,lmx[rs]);
/*向右子树或向虚边最远能走多远*/
lmx[x]=max(lmx[ls],sum[ls]+len[x]+R);
/*不经过x,或经过x并走到最远
注意左子树在原树中是一条链!不存在路径重叠问题*/
rmx[x]=max(rmx[rs],sum[rs]+L);
/*同理*/
mxs[x]=max(rmx[ls]+len[x]+R,lmx[rs]+L);
cmax(mxs[x],max(mxs[ls],mxs[rs]));
cmax(mxs[x],fir(path[x]));
cmax(mxs[x],fir(chain[x])+sec(chain[x]));
/*最长链和次长链可以形成一条路径*/
if(w[x]==) cmax(mxs[x],max(fir(chain[x]),));
/*用一堆东西来更新答案*/
}
void rotate(int x){
int y=fa[x],z=fa[y],d=ch[y][]==x;
if(!isroot(y)) ch[z][ch[z][]==y]=x;
fa[x]=z,fa[y]=x,fa[ch[x][d^]]=y,ch[y][d]=ch[x][d^],ch[x][d^]=y,pushup(y);
}
void splay(int x){
for(int y=fa[x],z=fa[y];!isroot(x);y=fa[x],z=fa[y]){
if(!isroot(y))
((ch[y][]==x)^(ch[z][]==y))?rotate(x):rotate(y);
rotate(x);
}
pushup(x);
}
void access(int x){
for(int y=;x;x=fa[y=x]){
splay(x);
if(rs) path[x].insert(mxs[rs]),chain[x].insert(lmx[rs]);
if(y) path[x].erase(path[x].find(mxs[y])),chain[x].erase(chain[x].find(lmx[y]));
rs=y,pushup(x);
/*注意虚实子树变换时要更新path和chain*/
}
}
void modify(int x){
/*改变点的颜色,col为1表示黑,0表示白*/
access(x),splay(x);
col[x]^=,w[x]=col[x]?(-inf):;
pushup(x),ans=mxs[x];
}
void dfs(int u){
for(int i=head[u];i;i=Next[i]){
int v=ver[i];
if(v==fa[u]) continue;
fa[v]=u,len[v]=edge[i],dfs(v);
/*把边的权值给儿子*/
chain[u].insert(lmx[v]),path[u].insert(mxs[v]);
}
pushup(u);
}
int main(){
//freopen("testdata.in","r",stdin);
n=read();init();
for(int i=;i<n;++i){
int u=read(),v=read(),e=read();
add(u,v,e),add(v,u,e);
}
dfs(),ans=mxs[];int q=read();
while(q--){
char op=getc();int x;
getc();
if(op=='A'){
ans<?puts("They have disappeared."):printf("%d\n",ans);
}
else x=read(),modify(x);
}
return ;
}

SP2666 QTREE4 - Query on a tree IV(LCT)的更多相关文章

  1. SP16580 QTREE7 - Query on a tree VII(LCT)

    题意翻译 一棵树,每个点初始有个点权和颜色(输入会给你) 0 u:询问所有u,v路径上的最大点权,要满足u,v路径上所有点颜色相同 1 u:反转u的颜色 2 u w:把u的点权改成w 题解 Qtree ...

  2. SP16549 QTREE6 - Query on a tree VI(LCT)

    题意翻译 题目描述 给你一棵n个点的树,编号1~n.每个点可以是黑色,可以是白色.初始时所有点都是黑色.下面有两种操作请你操作给我们看: 0 u:询问有多少个节点v满足路径u到v上所有节点(包括)都拥 ...

  3. 洛谷 P2056 [ZJOI2007]捉迷藏 || bzoj 1095: [ZJOI2007]Hide 捉迷藏 || 洛谷 P4115 Qtree4 || SP2666 QTREE4 - Query on a tree IV

    意识到一点:在进行点分治时,每一个点都会作为某一级重心出现,且任意一点只作为重心恰好一次.因此原树上任意一个节点都会出现在点分树上,且是恰好一次 https://www.cnblogs.com/zzq ...

  4. QTREE5 - Query on a tree V(LCT)

    题意翻译 你被给定一棵n个点的树,点从1到n编号.每个点可能有两种颜色:黑或白.我们定义dist(a,b)为点a至点b路径上的边个数. 一开始所有的点都是黑色的. 要求作以下操作: 0 i 将点i的颜 ...

  5. 【SPOJ QTREE4】Query on a tree IV(树链剖分)

    Description 给出一棵边带权(\(c\))的节点数量为 \(n\) 的树,初始树上所有节点都是白色.有两种操作: C x,改变节点 \(x\) 的颜色,即白变黑,黑变白. A,询问树中最远的 ...

  6. SPOJ QTREE4 Query on a tree IV ——动态点分治

    [题目分析] 同bzoj1095 然后WA掉了. 发现有负权边,只好把rmq的方式改掉. 然后T了. 需要进行底(ka)层(chang)优(shu)化. 然后还是T 下午又交就A了. [代码] #in ...

  7. 2019.02.16 spoj Query on a tree IV(链分治)

    传送门 题意简述: 捉迷藏强化版(带有边权,可以为负数) 思路:好吧这次我们不用点分树,我们用听起来更屌的链分治. 直接把树剖成若干条重链,这样保证从任意一个点跳到根节点是不会跳超过logloglog ...

  8. SPOJ QTREE4 - Query on a tree IV

    You are given a tree (an acyclic undirected connected graph) with N nodes, and nodes numbered 1,2,3. ...

  9. SPOJ QTREE4 - Query on a tree IV 树分治

    题意: 给出一棵边带权的树,初始树上所有节点都是白色. 有两种操作: C x,改变节点x的颜色,即白变黑,黑变白 A,询问树中最远的两个白色节点的距离,这两个白色节点可以重合(此时距离为0). 分析: ...

随机推荐

  1. re模块之research

    2. re.research re.research扫描整个字符串并返回第一个成功的匹配. 2.1函数语法: re.search(pattern, string, flags=0) 参数 描述 pat ...

  2. emulator: Trying to vcpu execute at eip:6d4053

  3. Spark Streaming原理简析

    执行流程 数据的接收 StreamingContext实例化的时候,需要传入一个SparkContext,然后指定要连接的spark matser url,即连接一个spark engine,用于获得 ...

  4. unity与android交互总结

    http://www.jianshu.com/p/4739ce2f4cd1 http://www.cnblogs.com/suoluo/p/5443889.html http://www.th7.cn ...

  5. iOS 导航栏黑线,UIImage 枚举处理方式

      ios 找出导航栏下面的黑线(可隐藏,改变样式等) http://www.jianshu.com/p/effa4a48f1e3     设置UIImage的渲染模式:UIImage.renderi ...

  6. loadrunner添加load generator连接失败解决办法

    1.到防火墙设置里面“允许程序和功能通过windows防火墙”,然后添加Loadrunner Agent Procss,到列表中,在“专用”和“公用”打勾,然后重启一下LR和Loadrunner Ag ...

  7. loadrunner怎么进行内容检查

    运行测试时,常常需要验证某些内容是否出现在返回的页面上.内容检查验证脚本运行时 Web 页面上是否出现期望的信息.可以插入两种类型的内容检查:➤ 文本检查.检查文本字符串是否出现在 Web 页面上.➤ ...

  8. Ubuntu14.04下redis安装 配置, redis主从配置

    1.到官网下载redis源码包 wget http://download.redis.io/releases/redis-3.2.8.tar.gz 2.解压 并 编译 .tar.gz cd redis ...

  9. UDP问题

    这两天使用C#的UdpClient,本机的服务是采用MFC的socket发的,用C#做客户端,然后客户端启动时,出现该条错误信息 ==通常每个套接字地址(协议/网络地址/端口)只允许使用一次. 笔记的 ...

  10. Cocos2d-x-2.2.2开发环境配置

    1.安装各种软件: Android SDK Android NDK Apache Ant Python Eclipse(adt) Cygwin(可选) Java Cocos2d-x 2.系统环境变量配 ...