#点分树 or LCT#洛谷 4115 Qtree4
两次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的更多相关文章
- 洛谷.4115.Qtree4/BZOJ.1095.[ZJOI2007]Hide捉迷藏(动态点分治 Heap)
题目链接 洛谷 SPOJ BZOJ1095(简化版) 将每次Solve的重心root连起来,会形成一个深度为logn的树,就叫它点分树吧.. 我们对每个root维护两个东西: 它管辖的子树中所有白点到 ...
- 洛谷 4115 Qtree4——链分治
题目:https://www.luogu.org/problemnew/show/P4115 论文:https://wenku.baidu.com/view/1bc2e4ea172ded630b1cb ...
- 洛谷 P2056 [ZJOI2007]捉迷藏 || bzoj 1095: [ZJOI2007]Hide 捉迷藏 || 洛谷 P4115 Qtree4 || SP2666 QTREE4 - Query on a tree IV
意识到一点:在进行点分治时,每一个点都会作为某一级重心出现,且任意一点只作为重心恰好一次.因此原树上任意一个节点都会出现在点分树上,且是恰好一次 https://www.cnblogs.com/zzq ...
- 树状数组 洛谷P3616 富金森林公园
P3616 富金森林公园 题目描述 博艾的富金森林公园里有一个长长的富金山脉,山脉是由一块块巨石并列构成的,编号从1到N.每一个巨石有一个海拔高度.而这个山脉又在一个盆地中,盆地里可能会积水,积水也有 ...
- AC日记——【模板】树链剖分 洛谷 P3384
题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式 ...
- 【模板】树的重心 洛谷P1364 医院设置
P1364 医院设置 题目描述 设有一棵二叉树,如图: 其中,圈中的数字表示结点中居民的人口.圈边上数字表示结点编号,现在要求在某个结点上建立一个医院,使所有居民所走的路程之和为最小,同时约定,相邻接 ...
- [洛谷P1198/BZOJ1012][JSOI2008] 最大数 - 树状数组/线段树?
其实已经学了树状数组和线段树,然而懒得做题,所以至今没写多少博客 Description 现在请求你维护一个数列,要求提供以下两种操作: 1. 查询操作. 语法:Q L 功能:查询当前数列中末尾L个数 ...
- 洛谷P4344 脑洞治疗仪 [SHOI2015] 线段树+二分答案/分块
!!!一道巨恶心的数据结构题,做完当场爆炸:) 首先,如果你用位运算的时候不小心<<打成>>了,你就可以像我一样陷入疯狂的死循环改半个小时 然后,如果你改出来之后忘记把陷入死循 ...
- 洛谷 P5706 【深基2.例8】再分肥宅水
题目连接: P5706 [深基2.例8]再分肥宅水 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 我提交的: 1 #include<iostream> 2 #inclu ...
- 洛谷P1972 【[SDOI2009]HH的项链】
这道题想了很久,发题解是为了理解的更深刻一点...(管理放我过好嘛qwq) 步入正题:这道题应该是很多做法,我选择的是离线+树状数组. 首先输入数组.用fisrt数组先记录元素最开始出现的位置,对应的 ...
随机推荐
- win32-localtime的使用
下面的例子用于反映本地系统的日期格式变化 // locale test #include <stdio.h> #include <locale.h> #include < ...
- Jenkins流水线使用@Grab 导入Maven库
有个需求需要在pipeline中调用Java的SDK去执行业务 使用 @Grab 注解可以在Maven中导入Java 库, @Grab('org.apache.commons:commons-math ...
- C#程序全局异常处理—WPF和Web API两种模式
C#程序的全局异常处理,网上搜下资料都是一大堆,我这里最近也是独立做一个B/S结构的小项目, 后面又增加了需求用WPF实现相同的功能,这里将我所使用的全局异常处理方式做一个简短的总结分享. Web A ...
- SQL Server 连接数据库报错 (ObjectExplorer)
报错信息 无法访问数据库 ReportServer. (ObjectExplorer) 具体错误信息: 程序位置: 在 Microsoft.SqlServer.Management.UI.VSInte ...
- 用ABP Suite创建Blazor Server的应用程序
这个应用程序我们取名为BlazorOne,意思是集AuthServer.HttpApi Host和Blazor Server3个功能于一体的应用程序.因为ABP Suite支持另外一种模式,是把上述3 ...
- springboot-@Async默认线程池导致OOM问题
目录 内存溢出的三种类型: 初步分析: 代码分析: 最终解决办法: 内存溢出的三种类型: 第一种OutOfMemoryError: PermGen space,发生这种问题的原意是程序中使用了大量的j ...
- 【Azure Function】修改Function执行的Timeout时间
问题描述 Azure Function默认的Timeout时间是否可以调整呢? 问题解答 可以的,根据创建Function的时候选择的定价层不同,Function 默认的Timeout时间也不同. 消 ...
- Swagger (API框架,API 文档 与API 定义同步更新)
1.依赖 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring ...
- C++学习笔记之指针引用
目录 指针 指针定义 左值与右值 指针数组与数组指针 const与指针 指针的指针 NULL指针 内存泄漏(Memory Leak)问题 智能指针 引用 指针 指针定义 指针定义的基本形式:指针本身就 ...
- day01-1-需求分析和项目设计
满汉楼01 1.需求分析 满汉楼项目说明 因为javaGUI不是学习的重点,这里将继续使用控制台界面来代替界面和事件处理 完成的功能: 登录 订座 点餐 结账 查看账单等功能 在实际项目中,独立完成项 ...