【BZOJ1095】【ZJOI2007】捉迷藏
前言
好恶心的一道题,代码写了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】捉迷藏的更多相关文章
- 动态点分治:Bzoj1095: [ZJOI2007]Hide 捉迷藏
简介 这是我自己的一点理解,可能写的不好 点分治都学过吧.. 点分治每次找重心把树重新按重心的深度重建成了一棵新的树,称为分治树 这个树最多有log层... 动态点分治:记录下每个重心的上一层重心,这 ...
- 【BZOJ1095】捉迷藏(动态点分治)
[BZOJ1095]捉迷藏(动态点分治) 题面 BZOJ 题解 动态点分治板子题 假设,不考虑动态点分治 我们来想怎么打暴力: \(O(n)DP\)求树的最长链 一定都会.不想解释了 所以,利用上面的 ...
- 洛谷 P2056 [ZJOI2007]捉迷藏 解题报告
P2056 [ZJOI2007]捉迷藏 题目描述 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏.他们的家很大且构造很奇特,由\ ...
- 树上最长链 Farthest Nodes in a Tree LightOJ - 1094 && [ZJOI2007]捉迷藏 && 最长链
树上最远点对(树的直径) 做法1:树形dp 最长路一定是经过树上的某一个节点的. 因此: an1[i],an2[i]分别表示一个点向下的最长链和次长链,次长链不存在就设为0:这两者很容易求 an3[i ...
- [bzoj1095][ZJOI2007]Hide 捉迷藏 点分树,动态点分治
[bzoj1095][ZJOI2007]Hide 捉迷藏 2015年4月20日7,8876 Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiaji ...
- BZOJ1095:[ZJOI2007]Hide 捉迷藏(动态点分治)
Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩 捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条 ...
- BZOJ1095 [ZJOI2007]Hide 捉迷藏 动态点分治 堆
原文链接https://www.cnblogs.com/zhouzhendong/p/BZOJ1095.html 题目传送门 - BZOJ1095 题意 有 N 个点,每一个点是黑色或者白色,一开始所 ...
- BZOJ1095: [ZJOI2007]Hide 捉迷藏【线段树维护括号序列】【思维好题】
Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩 捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条 ...
- 【bzoj1095】 ZJOI2007—捉迷藏
http://www.lydsy.com/JudgeOnline/problem.php?id=1095 (题目链接) 题意 一棵树,求最远的两黑点之间的距离,每次可以将黑点染白或者将白点染黑. So ...
- BZOJ1095: [ZJOI2007]Hide 捉迷藏【动态点分治】
Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩 捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条 ...
随机推荐
- Luogu - P1018 乘积最大 - 题解
原文:https://www.luogu.org/problemnew/solution/P1018?page=7 题目:P1018[乘积最大] 前言: 这题的正解理论上说是DP,可是由于民间数据太水 ...
- 关于 Windows to go
1. 在宿主计算器的操作系统中访问 Windows to go 的磁盘 如题,如果需要在宿主计算器的操作系统中访问 Windows to go 的U盘(移动硬盘)中的文件,只需要打开磁盘管理,“更改驱 ...
- 【题解】Luogu P5471 [NOI2019]弹跳
原题传送门 先考虑部分分做法: subtask1: 暴力\(O(nm)\)枚举,跑最短路 subtask2: 吧一行的点压到vector中并排序,二分查找每一个弹跳装置珂以到达的城市,跑最短路 sub ...
- 基于Jquery的下拉列表控件(个人觉得实用)
Selectize.js Selectize 是一个基于 jQuery 的 <select> UI 控件,对于标签选择和下拉列表功能非常有用. Selectize 的目标是通过简单而强大的 ...
- C#安装和卸载windowsService的bat指令
只需新建2个文本文档,将2个指令分别复制进去,再将txt格式改为bat格式,以管理员身份运行 安装指令 %SystemRoot%\Microsoft.NET\Framework\v4.0.30319\ ...
- Puppet自动化管理配置
Puppet:开源系统配置和管理工具 随着虚拟化和云计算技术的兴起,计算机集群的自动化管理和配置成为了数据中心运维管理的热点.对于 IaaS.Paas.Saas 来说,随着业务需求的提升,后台计算机集 ...
- 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 ...
- IntelliJ Cannot find declaration to goto----解决方案
系统中已经有了该类库,还是找不到类提示 close the project in intellij. close intellij. go to the project folder and dele ...
- git 从远程克隆代码并实现分支开发,合并分支,上传本地代码到远程
首先确认你已经安装了git 1.克隆远程代码到本地的操作 git clone 地址 打开git操作命令行 鼠标右键点击 复制需要克隆的项目的地址类似下面的ssh 输入命令进行 ...
- UIDatePicker基本使用
UIDatePicker提供了一个快速选择日期和时间的控件,他是UIControl的子类,专门用于日期时间的选择.其样式可以通过UIDatePicker的属性进行灵活设置,同时也可以获取到当前UIDa ...