题目链接 洛谷

SPOJ

BZOJ1095(简化版)

将每次Solve的重心root连起来,会形成一个深度为logn的树,就叫它点分树吧。。

我们对每个root维护两个东西: 它管辖的子树中所有白点到它上一层(点分树中的fa[x])的距离(记这个为h1),以及它的所有子树的h1的最大值(这样就有过fa[x]的答案了)(记这个为h2)

要能随时插入与删除距离,同时要取最值,用堆维护即可。删除用另外一个堆实现。

需要有一个堆Ans统计答案,由每个root的h2去更新Ans。

修改时一个点只会影响其所有祖先节点(有\(O(\log n\))个),而且只需要修改值即可(不关心具体路径),于是直接用堆实现,复杂度\(O(\log^2n)\)

还需要查询任意两点间的距离,\(dis[a]+dis[b]-2*dis[lca]\)即可,用RMQ以\(O(1)\)查LCA(注意括号序空间是2倍的).

注意堆修改时一定要先消除它在其它堆里的贡献heap.top,修改完再加入heap.top。

另外在访问每个点x时在h2[x]中加入0,因为2个点才可以对答案有贡献,x初始为白点是合法的。同时在把它变为黑点时直接可以删0,避免讨论。

最远边权是负的答案应是0

Upd 6.30:这代码比较慢,有些可以优化的地方,但是我不想改了。。

树剖其实会比RMQ快吧。

能用链分治 或 线段树 括号序列做,快非常多。。以后再说吧

补了括号序列的做法:https://www.cnblogs.com/SovietPower/p/10333155.html。(但是只会做捉迷藏,不会Qtree4)

//7140ms    83.11MB
#include <queue>
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
const int N=1e5+5,MAXIN=3e5; int n,Min,root,sz[N],Enum,H[N],to[N<<1],nxt[N<<1],val[N<<1],fa[N];
int dis[N],lg[N<<1],tm,cnt,id[N],A[N<<1][18],pos[N];//RMQ for LCA(感觉写的好麻烦--)
bool sta[N],vis[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Heap
{
std::priority_queue<int> h,d;
inline void Insert(int x){
h.push(x);
}
inline void Delete(int x){
d.push(x);
}
inline int Size(){
return h.size()-d.size();
}
inline void Fix(){
while(d.size() && h.top()==d.top()) h.pop(),d.pop();
}
inline int Top(){
Fix(); return h.top();
}
inline void Pop(){
Fix(), h.pop();
}
inline int Sec(){
int t=Top(); h.pop();//注意必要的地方要fix
int t2=Top(); h.push(t);
return t2;
}
inline int Two(){
int t=Top(); h.pop();//注意必要的地方要fix
int t2=Top(); h.push(t);
return t+t2;
}
void Change(int x,bool s){
if(s) Delete(x);
else Insert(x);
}
}h1[N],h2[N],Ans; inline int read()
{
int now=0,f=1;register char c=gc();
for(;!isdigit(c);c=gc()) if(c=='-') f=-1;
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now*f;
}
inline void AddEdge(int u,int v,int w)
{
to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum, val[Enum]=w;
to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum, val[Enum]=w;
}
void pre_DFS(int x,int f)
{
int t=++tm; A[pos[x]=++cnt][0]=t, id[t]=x;
for(int i=H[x]; i; i=nxt[i])
if(to[i]!=f)
dis[to[i]]=dis[x]+val[i], pre_DFS(to[i],x), A[++cnt][0]=t;//访问完一棵子树时再加上
}
void Init_RMQ()
{
for(int i=2; i<=cnt; ++i) lg[i]=lg[i>>1]+1;//lg[]大小2N
for(int j=1; j<=lg[cnt]; ++j)
for(int i=cnt-(1<<j-1); i; --i)//cnt-(1<<j-1) not n-(1<<j-1)!
A[i][j]=std::min(A[i][j-1],A[i+(1<<j-1)][j-1]);
}
void Get_root(int x,int f,int tot)
{
int mx=0; sz[x]=1;
for(int v,i=H[x]; i; i=nxt[i])
if(!vis[v=to[i]]&&v!=f)
{
Get_root(v,x,tot), sz[x]+=sz[v];
if(sz[v]>mx) mx=sz[v];
}
mx=std::max(mx,tot-sz[x]);
if(mx<Min) Min=mx, root=x;
}
void DFS(int x,int f,int d,Heap &h)
{//初始全白 都加进去
h.Insert(d);
for(int i=H[x]; i; i=nxt[i])
if(!vis[to[i]]&&to[i]!=f)
DFS(to[i],x,d+val[i],h);
}
inline void Add_Ans(Heap &h){
// if(h.Size()>=2) Ans.Insert(h.Top()+h.Sec());
if(h.Size()>=2) Ans.Insert(h.Two());
}
inline void Del_Ans(Heap &h){
// if(h.Size()>=2) Ans.Delete(h.Top()+h.Sec());
if(h.Size()>=2) Ans.Delete(h.Two());
}
void Solve(int x)
{
vis[x]=1, h2[x].Insert(0);
for(int v,i=H[x]; i; i=nxt[i])
if(!vis[v=to[i]])
{
Heap tmp; DFS(v,x,val[i],tmp);
Min=N, Get_root(v,x,sz[v]), fa[root]=x;
h1[root]=tmp;//h1[root]
h2[x].Insert(tmp.Top());
Solve(root);
}
Add_Ans(h2[x]);
}
int LCA(int l,int r)
{
if(l>r) std::swap(l,r);
int k=lg[r-l+1];
return id[std::min(A[l][k],A[r-(1<<k)+1][k])];
}
int Calc_dis(int a,int b){
return dis[a]+dis[b]-2*dis[LCA(pos[a],pos[b])];
}
void Change(int x,bool s)
{
Del_Ans(h2[x]), h2[x].Change(0,s), Add_Ans(h2[x]);
for(int now=x; fa[now]; now=fa[now])
{
Del_Ans(h2[fa[now]]);//!
if(h1[now].Size()) h2[fa[now]].Delete(h1[now].Top());//更改前先删除
h1[now].Change(Calc_dis(x,fa[now]),s);//变得dis是x与fa[now]的,不是now与fa[now]的。。mdzz
if(h1[now].Size()) h2[fa[now]].Insert(h1[now].Top());
Add_Ans(h2[fa[now]]);
}
}
inline char get(){
char c=gc();
while(c!='A'&&c!='C') c=gc();
return c;
} int main()
{
n=read();
for(int u,v,w,i=1; i<n; ++i) u=read(),v=read(),w=read(),AddEdge(u,v,w);
pre_DFS(1,-1), Init_RMQ();
Min=N, Get_root(1,-1,n), Solve(root);
int m=read(),x,white=n;char opt[3];
while(m--)
{
// scanf("%s",opt);
if(/*opt[0]*/get()=='C') x=read(),Change(x,sta[x]^=1),white+=sta[x]?-1:1;
else if(!white) puts("They have disappeared.");
else printf("%d\n",white>1?std::max(0,Ans.Top()):0);
}
return 0;
}

BZOJ1095

//154024kb	13728ms
//Qtree4弱化版 由于路径长度均为1,用dep就可以处理RMQ
#include <queue>
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
const int N=1e5+5,MAXIN=1e6; int n,Min,root,sz[N],Enum,H[N],to[N<<1],nxt[N<<1],fa[N];
int dep[N],lg[N<<1],cnt,A[N<<1][18],pos[N];
bool sta[N],vis[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Heap
{
std::priority_queue<int> h,d;
inline void Insert(int x){
h.push(x);
}
inline void Delete(int x){
d.push(x);
}
inline int Size(){
return h.size()-d.size();
}
inline void Fix(){
while(d.size() && h.top()==d.top()) h.pop(),d.pop();
}
inline int Top(){
Fix(); return h.top();
}
inline int Sec(){
Fix();
int t=h.top(); h.pop();
int t2=h.top(); h.push(t);
return t2;
}
inline int Two(){
int t=Top(); h.pop();//注意必要的地方要fix
int t2=Top(); h.push(t);
return t+t2;
}
void Change(int x,bool s){
if(s) Delete(x);
else Insert(x);
}
}h1[N],h2[N],Ans; inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
inline void AddEdge(int u,int v)
{
to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum;
}
void pre_DFS(int x,int f)
{
A[pos[x]=++cnt][0]=dep[x];
for(int i=H[x]; i; i=nxt[i])
if(to[i]!=f)
dep[to[i]]=dep[x]+1, pre_DFS(to[i],x), A[++cnt][0]=dep[x];//访问完一棵子树时再加上
}
void Init_RMQ()
{
for(int i=2; i<=cnt; ++i) lg[i]=lg[i>>1]+1;
for(int j=1; j<=lg[cnt]; ++j)
for(int i=cnt-(1<<j-1); i; --i)
A[i][j]=std::min(A[i][j-1],A[i+(1<<j-1)][j-1]);
}
void Get_root(int x,int f,int tot)
{
int mx=0; sz[x]=1;
for(int v,i=H[x]; i; i=nxt[i])
if(!vis[v=to[i]]&&v!=f)
{
Get_root(v,x,tot), sz[x]+=sz[v];
if(sz[v]>mx) mx=sz[v];
}
mx=std::max(mx,tot-sz[x]);
if(mx<Min) Min=mx, root=x;
}
void DFS(int x,int f,int d,Heap &h)
{//初始全白 都加进去
h.Insert(d);
for(int i=H[x]; i; i=nxt[i])
if(!vis[to[i]]&&to[i]!=f)
DFS(to[i],x,d+1,h);
}
inline void Add_Ans(Heap &h){
// if(h.Size()>=2) Ans.Insert(h.Top()+h.Sec());
if(h.Size()>=2) Ans.Insert(h.Two());
}
inline void Del_Ans(Heap &h){
// if(h.Size()>=2) Ans.Delete(h.Top()+h.Sec());
if(h.Size()>=2) Ans.Delete(h.Two());
}
void Solve(int x)
{
vis[x]=1, h2[x].Insert(0);
for(int v,i=H[x]; i; i=nxt[i])
if(!vis[v=to[i]])
{
Heap tmp; DFS(v,x,1,tmp);
Min=N, Get_root(v,x,sz[v]), fa[root]=x;
h1[root]=tmp;//h1[root]
h2[x].Insert(tmp.Top());
Solve(root);
}
Add_Ans(h2[x]);
}
int LCA(int l,int r)
{
if(l>r) std::swap(l,r);
int k=lg[r-l+1];
return std::min(A[l][k],A[r-(1<<k)+1][k]);
}
int Calc_dis(int a,int b){
return dep[a]+dep[b]-2*LCA(pos[a],pos[b]);
}
void Change(int x,bool s)
{
Del_Ans(h2[x]), h2[x].Change(0,s), Add_Ans(h2[x]);
for(int now=x; fa[now]; now=fa[now])
{
Del_Ans(h2[fa[now]]);//!
if(h1[now].Size()) h2[fa[now]].Delete(h1[now].Top());//更改前先删除
h1[now].Change(Calc_dis(x,fa[now]),s);
if(h1[now].Size()) h2[fa[now]].Insert(h1[now].Top());
Add_Ans(h2[fa[now]]);
}
}
inline char get(){
char c=gc();
while(c!='G'&&c!='C') c=gc();
return c;
} int main()
{
n=read();
for(int u,v,i=1; i<n; ++i) u=read(),v=read(),AddEdge(u,v);
pre_DFS(1,-1), Init_RMQ();
Min=N, Get_root(1,-1,n), Solve(root);
int m=read(),x,white=n;
while(m--)
{
if(get()=='C') x=read(),Change(x,sta[x]^=1),white+=sta[x]?-1:1;
else if(!white) puts("They have disappeared.");
else printf("%d\n",white>1?Ans.Top():0);
}
return 0;
}

洛谷.4115.Qtree4/BZOJ.1095.[ZJOI2007]Hide捉迷藏(动态点分治 Heap)的更多相关文章

  1. BZOJ 1095 [ZJOI2007]Hide 捉迷藏 ——动态点分治

    [题目分析] 这题好基啊. 先把分治树搞出来.然后每个节点两个堆. 第一个堆保存这个块里的所有点(即分治树中的所有儿子)到分治树上的父亲的距离. 第二个堆保存分治树子树中所有儿子第一个堆的最大值. 建 ...

  2. BZOJ 1095: [ZJOI2007]Hide 捉迷藏 动态点分治+堆

    写了7k多,可以说是一己之力切掉了这道毒瘤题~ 开 $3$ 种堆,分别维护每个子树最大深度,以及每个节点在点分树中对父亲的贡献,和全局的最优解. 由于需要支持堆的删除,所以写起来特别恶心+麻烦. 细节 ...

  3. BZOJ 1095: [ZJOI2007]Hide 捉迷藏(动态点分治)

    传送门 解题思路 点分树其实就是在点分治的基础上,把重心连起来.这样树高是\(log\)的,可以套用数据结构进行操作.这道题是求最远距离,所以每个点维护两个堆,分别表示所管辖的子树的最远距离和到父节点 ...

  4. 【BZOJ1095】[ZJOI2007]Hide 捉迷藏 动态树分治+堆

    [BZOJ1095][ZJOI2007]Hide 捉迷藏 Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉 ...

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

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

  6. BZOJ 1095: [ZJOI2007]Hide 捉迷藏

    Description 一棵树,支持两个操作,修改一个点的颜色,问树上最远的两个白点距离. Sol 动态点分治. 动态点分治就是将每个重心连接起来,形成一个跟线段树类似的结构,当然它不是二叉的... ...

  7. BZOJ1095 [ZJOI2007]Hide 捉迷藏 动态点分治 堆

    原文链接https://www.cnblogs.com/zhouzhendong/p/BZOJ1095.html 题目传送门 - BZOJ1095 题意 有 N 个点,每一个点是黑色或者白色,一开始所 ...

  8. 【bzoj1095】[ZJOI2007]Hide 捉迷藏 动态点分治+堆

    题目描述 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条双向走廊组成,这 ...

  9. bzoj 1095 [ZJOI2007]Hide 捉迷藏(括号序列+线段树)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1095 [题意] 给定一棵树,树上颜色或白或黑而且可以更改,多个询问求最远黑点之间的距离 ...

随机推荐

  1. Dubbo服务容错(整合hystrix)

    简介:Hystrix旨在通过控制那些访问远程系统.服务和第三方库的节点从而对延迟和故障提供更强大的容错能力,Hystrix具备拥有回退机制和断路器功能的线程和信号隔离.请求缓存和请求打包以及监控和配置 ...

  2. SpringBoot定制错误页面

    (1)有模板引擎的情况下,例如404错误,将会在thymeleaf的templates的error下寻找404.html,如果找不到再寻找4xx.html *所有4开头的错误状态码如果找不到特定的ht ...

  3. 跟踪OceanLotus的新下载程序KerrDown

    攻击的方法 两种方法将KerrDown下载器传递给目标.一个是使用带有恶意宏的Microsoft Office文档,另一个是包含带有DLL side-loading合法程序的RAR存档 .对于RAR存 ...

  4. argv[1] 路径问题

    在看<学习opencv>一书时遇到一个小问题:函数只是通过argv传递参数来读取图片并显示,但是却一直弹出画布,没有图像. 如下:test.c # include<stdio.h&g ...

  5. Python3学习笔记14-迭代与列表生成式

    迭代 如果给定一个list或tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历我们称为迭代(Iteration) 在Python中,迭代是通过for...in来完成的. d = ...

  6. 如何消除手机设置的字体大小对Cordova app(Android)界面font-size的影响

    ===================== 更新分割线 =================== 现在发现其实不需要用安卓编辑器打开,也能找到这个文件,路径是platforms\android\Cord ...

  7. MyEclipse中如何配置默认jsp为UTF-8格式

  8. FreeSWITCH Git版本管理

    由于测试FreeSWITCH不同版本的需要,研究了下Git的使用,通过Git来管理所有的版本,方便了测试.以下就总结下具体的使用方法: 其中:git clone ..是现在git仓库:git tag ...

  9. Ex 6_19 至多用k枚硬币兑换价格_第七次作业

    子问题定义: 定义一个二维数组b,其中b[i][j]表示用i个硬币是否能兑换价格j,表示第i个币种的面值, 递归关系: 初值设定: 求解顺序: 按下标从小到大依次求解数组b每一列的值,最后二维数组b的 ...

  10. Numpy中stack(),hstack(),vstack()函数详解

    一`.stack 按指定维度堆叠数组.      stack(a, b) 维度计算 axis=0: 2*m*n axis=1:  m*2*n axis=-1: m*n*2 a = np.arange( ...