门
点分树不卡常就别想过了。。。
题目
问树中最远黑点对距离
带修改
题解
点分树入门作
原图先乱跑一次点分治,保存点分治的每个root之间的父子关系,得到一颗点分树。然后我们的原图除了求dis就可以不管了
学习题解中不认识的大佬所说的套路:
- 点分治得到点分树
- 每个点用 S 1 , S 2 S_1,S_2 S1,S2两个数据结构维护,依次容斥
- 修改和查询都是树高(logn) × \times ×数据结构修改查询复杂度
- 初始化可以视为n次修改
此题用堆维护
①第一种堆:维护当前节点的每个点分树子树中的maxdis
②第二种堆:维护当前节点的所有子树到其点分树Father的dis
我们需要求的是某个节点作为中间点,它的两个点分树子树中的maxdis之和最大,也需要用一个堆来维护
技巧
此题堆需要删除堆中间部分的数,可以多保存一个trash堆用来保存要删的数,用堆的时候判一下是否两堆顶相同,相同弹掉即可
#include<bits/stdc++.h>
#define re register
using namespace std;
const int N=1e5+10,M=2*N,INF=1e9;
int n,m;
int a[N];
int head[N],nex[M],to[M],tot;
inline void build(int u,int v){tot++;nex[tot]=head[u];to[tot]=v;head[u]=tot;}
//--
namespace Cutree{
int tp[N],sz[N],fa[N],dep[N],son[N],dfn[N];
void dfs1(int u,int f)
{
sz[u]=1;dep[u]=dep[f]+1;fa[u]=f;
for(re int i=head[u];i;i=nex[i])
{
int v=to[i];if(v==f)continue;
dfs1(v,u);sz[u]+=sz[v];
if(sz[v]>sz[son[u]])son[u]=v;
}
}
void dfs2(int u,int p)
{
tp[u]=p;dfn[u]=1;
if(son[u])dfs2(son[u],p);
for(re int i=head[u];i;i=nex[i])
{
int v=to[i];if(dfn[v])continue;
dfs2(v,v);
}
}
inline int lca(int x,int y)
{
while(tp[x]^tp[y])
{
if(dep[tp[x]]<dep[tp[y]])swap(x,y);
x=fa[tp[x]];
}
return dep[x]<dep[y]?x:y;
}
inline int dis(int x,int y){return dep[x]+dep[y]-2*dep[lca(x,y)];}
}
//---
#define ap n*2+1
priority_queue<int>t[N<<1],trash[N<<1];
inline void add(int u,int w){if(w>=0)t[u].push(w);}
inline void del(int u,int w)
{
if(w>=0&&!t[u].empty())
{
if(t[u].top()==w)t[u].pop();
else trash[u].push(w);
}
}
inline int top(int u)
{
while(!t[u].empty()&&!trash[u].empty()&&t[u].top()==trash[u].top())t[u].pop(),trash[u].pop();
return !t[u].empty()?t[u].top():-INF;
}
inline int second(int u)
{
int tmp1=top(u);
if(tmp1==-INF)return -INF;
t[u].pop();
int tmp2=top(u);
t[u].push(tmp1);
return tmp2;
}
//---
int size,root,maxx;
int Sz[N],Fa[N];
bool vis[N];
void find(int u,int f)
{
int tmp=0;Sz[u]=1;
for(re int i=head[u];i;i=nex[i])
{
int v=to[i];if(v==f||vis[v])continue;
find(v,u);
Sz[u]+=Sz[v];
tmp=max(tmp,Sz[v]);
}
tmp=max(tmp,size-Sz[u]);
if(tmp<maxx)root=u,maxx=tmp;
}
void solve(int u,int f)
{
vis[u]=1;Fa[u]=f;
for(re int i=head[u];i;i=nex[i])
{
int v=to[i];if(vis[v])continue;
size=Sz[v];maxx=INF;root=0;
find(v,u);solve(root,u);
}
}
inline void init()
{
Cutree::dfs1(1,0);
Cutree::dfs2(1,1);
size=n;maxx=INF;root=0;
find(1,0);solve(root,0);
}
//----
int last[N],pre;
inline void Q_add(int u)
{
add(u,0);
pre=top(u)+second(u);
if(pre!=last[u])del(ap,last[u]),add(ap,last[u]=pre);
for(re int i=u;Fa[i];i=Fa[i])
{
int dist=Cutree::dis(u,Fa[i]);
int pos=top(i+n);
add(i+n,dist);
int sop=top(i+n);
if(pos!=sop)del(Fa[i],pos),add(Fa[i],sop);
pre=top(Fa[i])+second(Fa[i]);
if(pre!=last[Fa[i]])del(ap,last[Fa[i]]),add(ap,last[Fa[i]]=pre);
}
}
inline void Q_del(int u)
{
del(u,0);
pre=top(u)+second(u);
if(pre!=last[u])del(ap,last[u]),add(ap,last[u]=pre);
for(re int i=u;Fa[i];i=Fa[i])
{
int dist=Cutree::dis(u,Fa[i]);
int pos=top(i+n);
del(i+n,dist);
int sop=top(i+n);
if(pos!=sop)del(Fa[i],pos),add(Fa[i],sop);
pre=top(Fa[i])+second(Fa[i]);
if(pre!=last[Fa[i]])del(ap,last[Fa[i]]),add(ap,last[Fa[i]]=pre);
}
}
char s[10];
int main()
{
scanf("%d",&n);
for(re int x,y,i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
build(x,y);build(y,x);
}
init();
for(int i=1;i<=n;i++)Q_add(i);
int g=n;
scanf("%d",&m);
while(m--)
{
scanf("%s",s);
if(s[0]=='C')
{
int x;scanf("%d",&x);
if(a[x]==0)Q_del(x),a[x]=1,g--;
else Q_add(x),a[x]=0,g++;
}
else
{
if(g==0)printf("-1\n");
else if(g==1)printf("0\n");
else printf("%d\n",top(ap));
}
}
}
最后当然是加火车头过的啦