两次LCT的Access操作就可以求LCA啦

题目

给出一棵树,支持单点反色和查询全局最远白点


分析(点分树)

点分树的做法就是考虑点分树上的父亲把子树分成若干个部分,

那么所谓的白点直径可以把子树的最长链拼接起来

那么开三个可删堆,一个维护点分子树到父亲的距离,

一个维护父亲的答案,取第一个堆的最大值以保证其中每个值所属子树各不相同,

一个维护全局答案也就是直接用第二个堆的最大值和次大值合并起来,注意舍弃不必要的操作减小常数


代码

#include <cstdio>
#include <cctype>
#include <queue>
#include <algorithm>
#define rr register
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<23,stdin)),p1==p2?EOF:*p1++)
using namespace std;
const char S[22]={84,104,101,121,32,104,97,118,101,32,100,105,115,97,112,112,101,97,114,101,100,'.'};
const int N=100011; bool v[N]; struct node{int y,w,next;}e[N<<1];
int big[N],siz[N],SIZ,dfn[N],Siz[N],fat[N],root,as[N],tot;
char buf[1<<23],puf[1<<23],*p1,*p2; int nowp=-1;
int dep[N],et=1,two[18],lg[N<<1],f[N<<1][18],n,m,cnt,dis[N];
inline signed iut(){
rr int ans=0,f=1; rr char c=getchar();
while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans*f;
}
inline void Flush(){fwrite(puf,1,nowp+1,stdout),nowp=-1;}
inline void Putchar(char x){
if (nowp==(1<<23)) Flush();
puf[++nowp]=x;
}
inline void print(int ans){
rr char dig[21]; rr int len=-1;
do{
dig[++len]=ans%10+48,ans/=10;
}while (ans);
while (len>=0) Putchar(dig[len--]);
}
inline void Max(int &a,int b){a=a>b?a:b;}
inline void Min(int &a,int b){a=a<b?a:b;}
inline signed Get_Min(int x,int y){return dep[x]<dep[y]?x:y;}
inline signed lca(int x,int y){
if (x>y) x^=y,y^=x,x^=y; rr int z=lg[y-x+1];
return Get_Min(f[x][z],f[y-two[z]+1][z]);
}
inline signed Dis(int x,int y){
rr int LCA=lca(dfn[x],dfn[y]);
return dis[x]+dis[y]-2*dis[LCA];
}
inline void dfs(int x,int fa){
siz[x]=1,big[x]=0;
for (rr int i=as[x];i;i=e[i].next)
if (e[i].y!=fa&&!v[e[i].y]){
dfs(e[i].y,x);
siz[x]+=siz[e[i].y];
Max(big[x],siz[e[i].y]);
}
Max(big[x],SIZ-siz[x]);
if (big[x]<=big[root]) root=x;
}
inline void dp(int x){
v[x]=1,Siz[x]=big[0];
for (rr int i=as[x];i;i=e[i].next)
if (!v[e[i].y]){
big[0]=SIZ=siz[e[i].y];
dfs(e[i].y,root=0),
fat[root]=x,dp(root);
}
}
inline void Dfs(int x,int fa){
f[dfn[x]=++tot][0]=x,dep[x]=dep[fa]+1;
for (rr int i=as[x];i;i=e[i].next)
if (e[i].y!=fa){
dis[e[i].y]=dis[x]+e[i].w;
Dfs(e[i].y,x),f[++tot][0]=x;
}
}
struct Heap{
priority_queue<int>q0,q1;
inline signed Size(){return q0.size()-q1.size();}
inline void Push(int x){q0.push(x);}
inline void Pop(int x){q1.push(x);}
inline signed Top(){
while (!q1.empty()&&q0.top()==q1.top()) q0.pop(),q1.pop();
return q0.top();
}
inline signed Pot(){
rr int x=Top(); Pop(x);
rr int ans=Top()+x;
Push(x);
return ans;
}
}A[N],B[N],Ans;
inline void Ans_Pop(int x){if (A[x].Size()>1) Ans.Pop(A[x].Pot());}
inline void Ans_Push(int x){if (A[x].Size()>1) Ans.Push(A[x].Pot());}
inline void switch_off(int x){
Ans_Pop(x),A[x].Push(0),Ans_Push(x);
for (rr int X=x;fat[X];X=fat[X]){
Ans_Pop(fat[X]);
int DIS=Dis(fat[X],x);
if (!B[X].Size())
B[X].Push(DIS),A[fat[X]].Push(DIS);
else{
int now=B[X].Top(); B[X].Push(DIS);
if (now<DIS)
A[fat[X]].Pop(now),A[fat[X]].Push(DIS);
}
Ans_Push(fat[X]);
}
}
inline void switch_on(int x){
Ans_Pop(x),A[x].Pop(0),Ans_Push(x);
for (rr int X=x;fat[X];X=fat[X]){
Ans_Pop(fat[X]);
int now=B[X].Top(),DIS=Dis(fat[X],x);
if (now>DIS) B[X].Pop(DIS);
else {
B[X].Pop(now);
if (!B[X].Size()) A[fat[X]].Pop(now);
else if (B[X].Top()<now)
A[fat[X]].Pop(now),A[fat[X]].Push(B[X].Top());
}
Ans_Push(fat[X]);
}
}
signed main(){
cnt=n=iut(),lg[0]=-1,two[0]=1,Ans.Push(0);
for (rr int i=1;i<18;++i) two[i]=two[i-1]<<1;
for (rr int i=1;i<n;++i){
rr int x=iut(),y=iut(),w=iut();
e[++et]=(node){y,w,as[x]},as[x]=et;
e[++et]=(node){x,w,as[y]},as[y]=et;
}
for (rr int i=1;i<=et;++i) lg[i]=lg[i>>1]+1;
big[0]=SIZ=n,Dfs(1,0);
for (rr int j=1;j<=lg[tot];++j)
for (rr int i=1;i+two[j]-1<=tot;++i)
f[i][j]=Get_Min(f[i][j-1],f[i+two[j-1]][j-1]);
dfs(1,root=0),dfs(root,0),dp(root);
for (rr int i=1;i<=n;++i) switch_off(i);
for (rr int Q=iut();Q;--Q){
rr char c=getchar();
while (!isalpha(c)) c=getchar();
if (c=='A'){
if (!cnt) for (rr int o=0;o<22;++o) Putchar(S[o]);
else if (cnt==1) Putchar(48);
else print(Ans.Top());
Putchar(10);
}else{
rr int x=iut(); v[x]^=1;
if (!v[x]) --cnt,switch_on(x);
else ++cnt,switch_off(x);
}
}
Flush();
return 0;
}

分析

但是在 QTREE4 - Query on a tree IV 就一直TLE。

考虑LCT,维护深度最浅/最深的白点的最大距离,由于虚子树的答案也要维护所以用两个可删堆记录链和答案,然后一样拼接一下

但是SPOJ上只能用multiset过,我也不知道为什么


代码

#include <cstdio>
#include <cctype>
#include <queue>
using namespace std;
const int N=100011,inf=1e9;
struct node{int y,w,next;}e[N<<1];
int W[N],a[N],as[N],n,et=1,ans;
int iut(){
int ans=0,f=1; char c=getchar();
while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans*f;
}
void print(int ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
struct Heap{
priority_queue<int>q0,q1;
int Size(){return q0.size()-q1.size();}
void Push(int x){q0.push(x);}
void Pop(int x){q1.push(x);}
int Top(){
while (!q1.empty()&&q0.top()==q1.top()) q0.pop(),q1.pop();
return q0.top();
}
int Pot(){
int x=Top(); Pop(x);
int ans=Top()+x;
Push(x);
return ans;
}
}A[N],B[N];
struct Splay{
int lmx[N],rmx[N],mx[N],son[N][2],fat[N],w[N],rev[N],stac[N],TOP;
bool unroot(int x){return son[fat[x]][0]==x||son[fat[x]][1]==x;}
bool Is_R(int x){return son[fat[x]][1]==x;}
void pup(int x){
w[x]=w[son[x][0]]+w[son[x][1]]+W[x];
int xu=a[x],now=0;//与左子树相连实际是其祖先需要加上到父节点的边
if (B[x].Size()) xu=max(xu,now=B[x].Top()),now=max(now,0);
int L=max(xu,W[x]+rmx[son[x][0]]),R=max(xu,lmx[son[x][1]]);
lmx[x]=max(lmx[son[x][0]],w[son[x][0]]+W[x]+R);
rmx[x]=max(rmx[son[x][1]],w[son[x][1]]+L);
mx[x]=max(L+lmx[son[x][1]],rmx[son[x][0]]+W[x]+R);
mx[x]=max(mx[x],max(mx[son[x][0]],mx[son[x][1]]));
if (A[x].Size()) mx[x]=max(mx[x],A[x].Top());
if (B[x].Size()>1) mx[x]=max(mx[x],B[x].Pot());
if (!a[x]) mx[x]=max(mx[x],now);
}
void Rev(int x){swap(son[x][0],son[x][1]),rev[x]^=1;}
void pdown(int x){
if (rev[x]){
if (son[x][0]) Rev(son[x][0]);
if (son[x][1]) Rev(son[x][1]);
rev[x]=0;
}
}
void rotate(int x){
int Fa=fat[x],FFa=fat[Fa],wh=Is_R(x),t=son[x][wh^1];
if (unroot(Fa)) son[FFa][Is_R(Fa)]=x;
son[x][wh^1]=Fa,son[Fa][wh]=t;
if (t) fat[t]=Fa; fat[Fa]=x,fat[x]=FFa;
pup(Fa);
}
void splay(int x){
int y=x; stac[TOP=1]=y;
while (unroot(y)) stac[++TOP]=y=fat[y];
for (;TOP;--TOP) pdown(stac[TOP]);
for (;unroot(x);rotate(x)){
int Fa=fat[x];
if (unroot(Fa)) rotate((Is_R(x)^Is_R(Fa))?x:Fa);
}
pup(x);
}
void Access(int x){
for (int y=0;x;x=fat[y=x]){
splay(x);
if (son[x][1]) A[x].Push(mx[son[x][1]]),B[x].Push(lmx[son[x][1]]);
if (y) A[x].Pop(mx[y]),B[x].Pop(lmx[y]);
son[x][1]=y,pup(x);
}
} }Tre;
void dfs(int x,int fa){
for (int i=as[x];i;i=e[i].next)
if (e[i].y!=fa){
Tre.fat[e[i].y]=x,W[e[i].y]=e[i].w,dfs(e[i].y,x);
A[x].Push(Tre.mx[e[i].y]),B[x].Push(Tre.lmx[e[i].y]);
}
Tre.pup(x);
}
int main(){
n=iut();
for (int i=1;i<n;++i){
int x=iut(),y=iut(),w=iut();
e[++et]=(node){y,w,as[x]},as[x]=et;
e[++et]=(node){x,w,as[y]},as[y]=et;
}
for (int i=0;i<=n;++i) Tre.lmx[i]=Tre.rmx[i]=Tre.mx[i]=-inf;
dfs(1,0),ans=Tre.mx[1];
for (int Q=iut();Q;--Q){
char ch=getchar();
while (!isalpha(ch)) ch=getchar();
if (ch=='A'){
if (ans<0) puts("They have disappeared.");
else print(ans),putchar(10);
}else{
int x=iut();
Tre.Access(x),Tre.splay(x),
a[x]=Tre.mx[0]-a[x],
Tre.pup(x),ans=Tre.mx[x];
}
}
return 0;
}

#点分树 or LCT#洛谷 4115 Qtree4的更多相关文章

  1. 洛谷.4115.Qtree4/BZOJ.1095.[ZJOI2007]Hide捉迷藏(动态点分治 Heap)

    题目链接 洛谷 SPOJ BZOJ1095(简化版) 将每次Solve的重心root连起来,会形成一个深度为logn的树,就叫它点分树吧.. 我们对每个root维护两个东西: 它管辖的子树中所有白点到 ...

  2. 洛谷 4115 Qtree4——链分治

    题目:https://www.luogu.org/problemnew/show/P4115 论文:https://wenku.baidu.com/view/1bc2e4ea172ded630b1cb ...

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

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

  4. 树状数组 洛谷P3616 富金森林公园

    P3616 富金森林公园 题目描述 博艾的富金森林公园里有一个长长的富金山脉,山脉是由一块块巨石并列构成的,编号从1到N.每一个巨石有一个海拔高度.而这个山脉又在一个盆地中,盆地里可能会积水,积水也有 ...

  5. AC日记——【模板】树链剖分 洛谷 P3384

    题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式 ...

  6. 【模板】树的重心 洛谷P1364 医院设置

    P1364 医院设置 题目描述 设有一棵二叉树,如图: 其中,圈中的数字表示结点中居民的人口.圈边上数字表示结点编号,现在要求在某个结点上建立一个医院,使所有居民所走的路程之和为最小,同时约定,相邻接 ...

  7. [洛谷P1198/BZOJ1012][JSOI2008] 最大数 - 树状数组/线段树?

    其实已经学了树状数组和线段树,然而懒得做题,所以至今没写多少博客 Description 现在请求你维护一个数列,要求提供以下两种操作: 1. 查询操作. 语法:Q L 功能:查询当前数列中末尾L个数 ...

  8. 洛谷P4344 脑洞治疗仪 [SHOI2015] 线段树+二分答案/分块

    !!!一道巨恶心的数据结构题,做完当场爆炸:) 首先,如果你用位运算的时候不小心<<打成>>了,你就可以像我一样陷入疯狂的死循环改半个小时 然后,如果你改出来之后忘记把陷入死循 ...

  9. 洛谷 P5706 【深基2.例8】再分肥宅水

    题目连接: P5706 [深基2.例8]再分肥宅水 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 我提交的: 1 #include<iostream> 2 #inclu ...

  10. 洛谷P1972 【[SDOI2009]HH的项链】

    这道题想了很久,发题解是为了理解的更深刻一点...(管理放我过好嘛qwq) 步入正题:这道题应该是很多做法,我选择的是离线+树状数组. 首先输入数组.用fisrt数组先记录元素最开始出现的位置,对应的 ...

随机推荐

  1. 【libGDX】Mesh立方体贴图(6张图)

    1 前言 ​ 本文通过一个立方体贴图的例子,讲解三维纹理贴图的应用,案例中使用 6 张不同的图片给立方体贴图,图片如下. ​ 读者如果对 libGDX 不太熟悉,请回顾以下内容. 使用Mesh绘制三角 ...

  2. 【Azure 应用服务】App Service for Windows 环境中为Tomcat自定义4xx/5xx页面

    问题描述 通过设置Java Web项目,实现在App Service For Windows环境中达到自定义4XX/5XX的页面效果 问题解答 第一步:在本地项目文件中打开web.xml文件 (src ...

  3. Codeforces Round 927 (Div. 3)(A~F)

    目录 A B C D E F A 第一个遇到连续两个荆棘的地方就不能再赢金币了. 所以统计连续两个荆棘之前的所有金币 #include <bits/stdc++.h> #define in ...

  4. MYSQL中正则表达式检索数据库

    1.MySQL中使用通配符检索数据库,之外还可以使用正则表达式来检索数据. 使用通配符   '_'  和   '%'的区别如下,   使用通配符的技巧:一般的来说 通配符可以处理数据,但是消耗内存较大 ...

  5. MarkDown --- 数学公式语法集

    介绍 Markdown 是一种轻量级标记语言,它允许你使用易于阅读.易于编写的纯文本格式来创建富文本内容.通过简单的标记符号,如井号(#).星号(*)和下划线(_),可以快速地添加标题.粗体.斜体.链 ...

  6. MySQL---面经

    如果想要对 MySQL 的索引树有更深入的了解,掘金的小册子:<MySQL 是怎样运行的> MySQL 是怎样运行的 以下是常见面试题 MySQL日志 MySQL日志系统 redo_log ...

  7. ansible 自动化运维(2)

    回到顶部 Ansible playbook 简介 playbook 是 ansible 用于配置,部署,和管理被控节点的剧本. 通过 playbook 的详细描述,执行其中的一系列 tasks ,可以 ...

  8. redis三主三从详细搭建过程

    搭建Redis三主三从集群的详细步骤如下: 准备环境: 确保你有六台服务器或虚拟机,每台服务器上都已经安装了Redis.这些服务器将用于搭建三主三从的Redis集群. 确保所有服务器之间的网络连接正常 ...

  9. 基于ADS1299的可穿戴设备调试之接口含义简析

    前言    几个项目都用到了ADS1299,没想到中间会出那么多的问题.在解决问题的时候,这里面暴露了团队的不少不足之处.看来做技术,还是需要不断地积累.思维不能留盲点啊.要经常总结,做笔记.   接 ...

  10. 1、Azure Devops之什么是Azure DevOps

    什么是Azure DevOps 1.师出名门:是微软推出的一个集项目管理.开发管理.测试管理的一个服务套件. 2.历史:前身是微软在2005年推出的Team foundation Server一个专门 ...