前言

好恶心的一道题,代码写了2.5h,调试调了5h+,局部重构了n遍。

题意

一棵树上的节点有黑白两色,初始为黑,支持修改颜色,查询最远黑点对。$n<=10^5,m<=5*10^5$

题解

ver 1

先考虑查询,可以在每个点保存一个堆s1存储子树内到这个点的路径。

再维护一个全局堆,把每个点的【最大和次大的值的和】放入堆内。

查询时直接取出堆顶。

ver 2

然后你发现你遇到了这种情况(1-2的路径被重复走了)

于是在每个点再开一个新的堆s2储存它的子数内到父亲的路径。

每个点的s1从儿子的s2.top()中取得。

这样每个点只会对父亲做一次贡献(这好像是一个常见的套路)

要修改的话就从这个点开始一直往父亲走,在走的时候顺便更新(具体看代码中的on和off)

ver 3

然后你发现你的代码时空复杂度都是$O(n^2)$的

于是用点分治重构树,这样树高就是$O(logn)$,空间复杂度均摊就是$O(n*logn)$的了。

这样求距离就要用lca了,时间复杂度$O(n*logn^2)$。

注意一定要用堆,平衡树会超时(我在这调了2h+)

这里用到了一个小技巧:可删除任意元素的堆。

具体就是对于每个堆再维护一个堆用于存储要删除的节点。

在取top的时候看看top是否与删除堆的top相等,如相等,则删除这个top。

代码

//#pragma comment(linker, "/STACK:268435456,268435456")
#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
using namespace std;
#define N 100010
#define int long long
class _set
{
public:
priority_queue<int> source,del;
int top()
{
while(!del.empty()&&source.top()==del.top()) source.pop(),del.pop();
return source.top();
}
int size()
{
return source.size()-del.size();
}
int sec()
{
int t=top(),ret;
del.push(t);
ret=top();
source.push(t);
return ret;
}
}s1[N],s2[N],ans;
#define _erase del.push
#define _push source.push
int to[N*2],nxt[N*2],head[N],cnt;
void connect(int a,int b)
{
for(int i=1;i<=2;i++)
{
to[++cnt]=b;
nxt[cnt]=head[a];
head[a]=cnt;
swap(a,b);
}
}
int sz[N],root,used[N],fa[N],tot,up[N][21],dep[N];
void dfs(int id,int from)
{
//printf("%d\n",id);
up[id][0]=from;
dep[id]=dep[from]+1;
for(int i=1;i<=20;i++) up[id][i]=up[up[id][i-1]][i-1];
for(int i=head[id];i;i=nxt[i])
{
if(to[i]==from) continue;
dfs(to[i],id);
}
}
int get_lca(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
for(int i=20;dep[x]>dep[y];i--)
{
if(dep[up[x][i]] >= dep[y]) x=up[x][i];
}
if(x==y) return x;
for(int i=20;up[x][0]!=up[y][0];i--)
{
if(up[x][i]!=up[y][i])
{
x=up[x][i];
y=up[y][i];
}
}
return up[x][0];
}
int get_dis(int x,int y)
{
int lca=get_lca(x,y);
return dep[x]+dep[y]-2*dep[lca];
}
void del(_set &s)
{
ans._erase(s.top()+s.sec());
}
void add(_set &s)
{
ans._push(s.top()+s.sec());
}
void get_root(int id,int from,int size)
{
sz[id]=1;
int maxn=0;
for(int i=head[id];i;i=nxt[i])
{
if(to[i]==from||used[to[i]]) continue;
get_root(to[i],id,size);
sz[id]+=sz[to[i]];
maxn=max(maxn,sz[to[i]]);
}
if(max(maxn,size-sz[id])<=size/2)
root=id;
}
void get_size(int id,int from)
{
sz[id]=1;
for(int i=head[id];i;i=nxt[i])
{
if(to[i]==from||used[to[i]]) continue;
get_size(to[i],id);
sz[id]+=sz[to[i]];
}
}
void divide(int id)
{
used[id]=true;
for(int i=head[id];i;i=nxt[i])
{
if(used[to[i]]) continue;
//puts("$");
get_root(to[i],id,sz[to[i]]);
fa[root]=id;
get_size(root,0);
divide(root);
}
}
void off(int id)
{
s1[id].source.push(0);//允许路径在这里停下
if(s1[id].size()==2) add(s1[id]);//如果之前没有添加到答案堆,则添加
for(int t=id;fa[t];t=fa[t])//一路往上更新父亲以及答案堆
{
if(s1[fa[t]].size()>=2) del(s1[fa[t]]);//将答案堆中fa[t]旧的数据删除
if(s2[t].size()) s1[fa[t]].del.push(s2[t].top()); //将s1[fa[t]]中s2[t]旧的数据删除
s2[t]._push(get_dis(id,fa[t]));//因为现在路径可以从id开始,所以添加一条id->fa[t]的路径
s1[fa[t]]._push(s2[t].top());//将新的数据加入
if(s1[fa[t]].size()>=2) add(s1[fa[t]]);//同上
}
}
void on(int id)//基本同上
{
if(s1[id].size()==2) del(s1[id]);
s1[id]._erase(0);
for(int t=id;fa[t];t=fa[t])
{
if(s1[fa[t]].size()>=2) del(s1[fa[t]]);
if(s2[t].size()) s1[fa[t]]._erase(s2[t].top());
s2[t]._erase(get_dis(id,fa[t]));
if(s2[t].size()) s1[fa[t]]._push(s2[t].top());
if(s1[fa[t]].size()>=2) add(s1[fa[t]]);
}
}
int sta[N];
signed main()
{
int n,q;
cin>>n;
tot=n;
for(int i=1;i<n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
connect(a,b);
}
dfs(1,0);
get_root(1,0,n);
get_size(root,0);
divide(root);
cin>>q;
for(int i=1;i<=n;i++) off(i);
for(int i=1;i<=q;i++)
{
char opt;
scanf(" %c",&opt);
int p;
if(opt=='G')
{
if(tot>=2) printf("%d\n", ans.top());
else if(tot==1) puts("0");
else puts("-1");
}
else
{
scanf("%d",&p);
if(sta[p]) off(p),tot++;
else on(p),tot--;
sta[p]^=1;
}
}
}

  

【BZOJ1095】【ZJOI2007】捉迷藏的更多相关文章

  1. 动态点分治:Bzoj1095: [ZJOI2007]Hide 捉迷藏

    简介 这是我自己的一点理解,可能写的不好 点分治都学过吧.. 点分治每次找重心把树重新按重心的深度重建成了一棵新的树,称为分治树 这个树最多有log层... 动态点分治:记录下每个重心的上一层重心,这 ...

  2. 【BZOJ1095】捉迷藏(动态点分治)

    [BZOJ1095]捉迷藏(动态点分治) 题面 BZOJ 题解 动态点分治板子题 假设,不考虑动态点分治 我们来想怎么打暴力: \(O(n)DP\)求树的最长链 一定都会.不想解释了 所以,利用上面的 ...

  3. 洛谷 P2056 [ZJOI2007]捉迷藏 解题报告

    P2056 [ZJOI2007]捉迷藏 题目描述 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏.他们的家很大且构造很奇特,由\ ...

  4. 树上最长链 Farthest Nodes in a Tree LightOJ - 1094 && [ZJOI2007]捉迷藏 && 最长链

    树上最远点对(树的直径) 做法1:树形dp 最长路一定是经过树上的某一个节点的. 因此: an1[i],an2[i]分别表示一个点向下的最长链和次长链,次长链不存在就设为0:这两者很容易求 an3[i ...

  5. [bzoj1095][ZJOI2007]Hide 捉迷藏 点分树,动态点分治

    [bzoj1095][ZJOI2007]Hide 捉迷藏 2015年4月20日7,8876 Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiaji ...

  6. BZOJ1095:[ZJOI2007]Hide 捉迷藏(动态点分治)

    Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩 捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条 ...

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

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

  8. BZOJ1095: [ZJOI2007]Hide 捉迷藏【线段树维护括号序列】【思维好题】

    Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩 捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条 ...

  9. 【bzoj1095】 ZJOI2007—捉迷藏

    http://www.lydsy.com/JudgeOnline/problem.php?id=1095 (题目链接) 题意 一棵树,求最远的两黑点之间的距离,每次可以将黑点染白或者将白点染黑. So ...

  10. BZOJ1095: [ZJOI2007]Hide 捉迷藏【动态点分治】

    Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩 捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条 ...

随机推荐

  1. Luogu - P1018 乘积最大 - 题解

    原文:https://www.luogu.org/problemnew/solution/P1018?page=7 题目:P1018[乘积最大] 前言: 这题的正解理论上说是DP,可是由于民间数据太水 ...

  2. 关于 Windows to go

    1. 在宿主计算器的操作系统中访问 Windows to go 的磁盘 如题,如果需要在宿主计算器的操作系统中访问 Windows to go 的U盘(移动硬盘)中的文件,只需要打开磁盘管理,“更改驱 ...

  3. 【题解】Luogu P5471 [NOI2019]弹跳

    原题传送门 先考虑部分分做法: subtask1: 暴力\(O(nm)\)枚举,跑最短路 subtask2: 吧一行的点压到vector中并排序,二分查找每一个弹跳装置珂以到达的城市,跑最短路 sub ...

  4. 基于Jquery的下拉列表控件(个人觉得实用)

    Selectize.js Selectize 是一个基于 jQuery 的 <select> UI 控件,对于标签选择和下拉列表功能非常有用. Selectize 的目标是通过简单而强大的 ...

  5. C#安装和卸载windowsService的bat指令

    只需新建2个文本文档,将2个指令分别复制进去,再将txt格式改为bat格式,以管理员身份运行 安装指令 %SystemRoot%\Microsoft.NET\Framework\v4.0.30319\ ...

  6. Puppet自动化管理配置

    Puppet:开源系统配置和管理工具 随着虚拟化和云计算技术的兴起,计算机集群的自动化管理和配置成为了数据中心运维管理的热点.对于 IaaS.Paas.Saas 来说,随着业务需求的提升,后台计算机集 ...

  7. SAP HANA学习资料大全 Simple Finane + Simple Logisitic [非常完善的学习资料汇总]

    Check out this SDN blog if you plan to write HANA Certification exam http://scn.sap.com/community/ha ...

  8. IntelliJ Cannot find declaration to goto----解决方案

    系统中已经有了该类库,还是找不到类提示 close the project in intellij. close intellij. go to the project folder and dele ...

  9. git 从远程克隆代码并实现分支开发,合并分支,上传本地代码到远程

    首先确认你已经安装了git 1.克隆远程代码到本地的操作 git clone 地址   打开git操作命令行 鼠标右键点击        复制需要克隆的项目的地址类似下面的ssh     输入命令进行 ...

  10. UIDatePicker基本使用

    UIDatePicker提供了一个快速选择日期和时间的控件,他是UIControl的子类,专门用于日期时间的选择.其样式可以通过UIDatePicker的属性进行灵活设置,同时也可以获取到当前UIDa ...