Luogu P2056 [ZJOI2007]捉迷藏
入坑动态点分治的题目,感觉还不错被卡常后重构代码
首先静态点分治相信大家肯定都会,就是不断找重心然后暴力计算每棵子树内的贡献。
这题如果只有单次询问,我们很容易想到对于每个分治中心的所以儿子的子树中找两条最长链拼起来。
或者是直接以这个点为端点的一条链的最大值。
如果就这么做复杂度将达到\(O(qn\log n)\),完全无法接受。
我们还是考虑利用一下这个思想,点分治的优化时间的主要方式就是它这棵递归树高度均衡,可以直接在上面完成暴力操作。
注意到这道题没有动态加边,所以其实树的形态是不会变化的,所以我们可以在开始时先跑一边静态的点分治,在上面计算答案的同时把每次的分支中心的树结构建出来。
(在实际题目中由于我们一般只是暴力跳父亲节点,所以可以只记录父节点)
然后这个看似暴力的东西其实就是传说中的点分树
那么动态的问题就比较简单了,我们每次开关一盏灯的时候会影响到点分树上一条链的信息。直接跳即可。
考虑维护答案,这个也很简单,我们开三个大根堆,一个维护子树内最长链,一个维护到父节点的最长链,还有一个维护所有的答案。
统计答案的时候每个子树只用取最大值和次大值即可。不过要支持删除,可以考虑再开一个维护删除标记的堆免去手写烦恼。
两点间的距离在DFS的时候记录一个到根节点的路径长然后用LCA差分算一下就好了。
总体复杂度为\(O(n\log^2 n)\),足以通过本题。
先送上最早写的思路比较清晰的CODE,但一直被卡\(90pts\)
#include<cstdio>
#include<cctype>
#include<queue>
#define RI register int
#define Tp template <typename T>
#define add(x,y) e[++cnt]=(edge){y,head[x]},head[x]=cnt
using namespace std;
const int N=100005,INF=1e9;
struct edge
{
int to,nxt;
}e[N<<1]; int head[N],n,m,rt,sonsize,mx[N],cnt,x,y,num; bool is_light[N]; char opt;
class FileInputOutput
{
private:
static const int S=1<<21;
#define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
#define pc(ch) (Ftop<S?Fout[Ftop++]=ch:(fwrite(Fout,1,S,stdout),Fout[(Ftop=0)++]=ch))
char Fin[S],Fout[S],*A,*B; int pt[15],Ftop;
public:
Tp inline void read(T &x)
{
x=0; char ch; while (!isdigit(ch=tc()));
while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
}
Tp inline void write(T x)
{
if (!x) return (void)(pc('0'),pc('\n')); if (x<0) pc('-'),x=-x; RI ptop=0;
while (x) pt[++ptop]=x%10,x/=10; while (ptop) pc(pt[ptop--]+48); pc('\n');
}
inline void get_alpha(char &ch)
{
while (!isalpha(ch=tc()));
}
inline void Fend(void)
{
fwrite(Fout,1,Ftop,stdout);
}
#undef tc
#undef pc
}F;
struct Heap
{
priority_queue <int> big,del;
inline void push(int x)
{
big.push(x);
}
inline void erase(int x)
{
del.push(x);
}
inline int top(void)
{
while (!del.empty()&&big.top()==del.top())
big.pop(),del.pop(); return big.top();
}
inline int sectop(void)
{
int t=top(); pop(); int ret=top(); push(t); return ret;
}
inline void pop(void)
{
while (!del.empty()&&big.top()==del.top())
big.pop(),del.pop(); big.pop();
}
inline int size(void)
{
return big.size()-del.size();
}
inline bool empty(void)
{
return size()?0:1;
}
}T[N],M[N],A;
class LCA_Solver
{
private:
static const int P=17;
int anc[N][P],dep[N];
inline void swap(int &x,int &y)
{
int t=x; x=y; y=t;
}
inline void reset(int now)
{
for (RI i=0;i<P-1;++i) if (anc[now][i])
anc[now][i+1]=anc[anc[now][i]][i]; else break;
}
inline int query(int x,int y)
{
RI i; if (dep[x]<dep[y]) swap(x,y); for (i=P-1;~i;--i)
if (dep[anc[x][i]]>=dep[y]) x=anc[x][i]; if (x==y) return x;
for (i=P-1;~i;--i) if (anc[x][i]!=anc[y][i])
x=anc[x][i],y=anc[y][i]; return anc[x][0];
}
public:
#define to e[i].to
inline void DFS(int now,int fa)
{
dep[now]=dep[fa]+1; reset(now);
for (RI i=head[now];i;i=e[i].nxt)
if (to!=fa) anc[to][0]=now,DFS(to,now);
}
#undef to
inline int dis(int x,int y)
{
return dep[x]+dep[y]-(dep[query(x,y)]<<1);
}
}L;
inline void insert(Heap &s)
{
if (s.size()>1) A.push(s.top()+s.sectop());
}
inline void remove(Heap &s)
{
if (s.size()>1) A.erase(s.top()+s.sectop());
}
class Point_Division_Solver
{
private:
int size[N],tofa[N]; bool vis[N];
inline void maxer(int &x,int y)
{
if (y>x) x=y;
}
#define to e[i].to
inline void travel(int now,int fa,int fart)
{
T[rt].push(L.dis(now,fart)); for (RI i=head[now];i;i=e[i].nxt)
if (to!=fa&&!vis[to]) travel(to,now,fart);
}
public:
inline void getrt(int now,int fa)
{
size[now]=1; mx[now]=0; for (RI i=head[now];i;i=e[i].nxt)
if (to!=fa&&!vis[to]) getrt(to,now),size[now]+=size[to],maxer(mx[now],size[to]);
if (maxer(mx[now],sonsize-size[now]),mx[now]<mx[rt]) rt=now;
}
inline void solve(int now,int fa)
{
tofa[now]=fa; vis[now]=1; M[now].push(0); travel(now,fa,fa);
for (RI i=head[now];i;i=e[i].nxt) if (to!=fa&&!vis[to])
mx[rt=0]=INF,sonsize=size[to],getrt(to,now),to=rt,solve(rt,now),M[now].push(T[to].top()); insert(M[now]);
}
#undef to
inline void Off(int now)
{
remove(M[now]); M[now].push(0); insert(M[now]);
for (RI i=now;i;i=tofa[i])
{
remove(M[tofa[i]]); if (!T[i].empty()) M[tofa[i]].erase(T[i].top());
T[i].push(L.dis(now,tofa[i])); if (!T[i].empty())
M[tofa[i]].push(T[i].top()); insert(M[tofa[i]]);
}
}
inline void On(int now)
{
remove(M[now]); M[now].erase(0); insert(M[now]);
for (RI i=now;i;i=tofa[i])
{
remove(M[tofa[i]]); if (!T[i].empty()) M[tofa[i]].erase(T[i].top());
T[i].erase(L.dis(now,tofa[i])); if (!T[i].empty())
M[tofa[i]].push(T[i].top()); insert(M[tofa[i]]);
}
}
}S;
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
RI i; for (F.read(n),num=n,i=1;i<n;++i) F.read(x),F.read(y),add(x,y),add(y,x);
for (sonsize=n,L.DFS(1,0),mx[rt=0]=INF,S.getrt(1,0),S.solve(rt,0),F.read(m),i=1;i<=m;++i)
{
F.get_alpha(opt); if (opt^'C') { F.write(num<=1?num-1:A.top()); continue; }
F.read(x); if (is_light[x]) S.Off(x),++num; else S.On(x),--num; is_light[x]^=1;
}
return F.Fend(),0;
}
后来没办法重构了代码,加了一堆类似剪枝的东西上去终于搞过去了。
CODE
#include<cstdio>
#include<cctype>
#include<queue>
#define RI register int
#define Tp template <typename T>
#define add(x,y) e[++cnt]=(edge){y,head[x]},head[x]=cnt
using namespace std;
const int N=100005,INF=1e9;
struct edge
{
int to,nxt;
}e[N<<1]; int head[N],n,m,rt,sonsize,mx[N],cnt,x,y,num; bool is_light[N]; char opt;
class FileInputOutput
{
private:
static const int S=1<<21;
#define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
#define pc(ch) (Ftop<S?Fout[Ftop++]=ch:(fwrite(Fout,1,S,stdout),Fout[(Ftop=0)++]=ch))
char Fin[S],Fout[S],*A,*B; int pt[15],Ftop;
public:
Tp inline void read(T &x)
{
x=0; char ch; while (!isdigit(ch=tc()));
while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
}
Tp inline void write(T x)
{
if (!x) return (void)(pc('0'),pc('\n')); if (x<0) pc('-'),x=-x; RI ptop=0;
while (x) pt[++ptop]=x%10,x/=10; while (ptop) pc(pt[ptop--]+48); pc('\n');
}
inline void get_alpha(char &ch)
{
while (!isalpha(ch=tc()));
}
inline void Fend(void)
{
fwrite(Fout,1,Ftop,stdout);
}
#undef tc
#undef pc
}F;
struct Heap
{
priority_queue <int> big,del;
inline void push(int x)
{
big.push(x);
}
inline void erase(int x)
{
del.push(x);
}
inline int top(void)
{
while (!del.empty()&&!(big.top()^del.top()))
big.pop(),del.pop(); return big.empty()?0:big.top();
}
inline int get(void)
{
int s=size(); if (!s) return 0; if (s==1) return top();
int t=top(); pop(); int ret=top()+t; push(t); return ret;
}
inline void pop(void)
{
while (!del.empty()&&!(big.top()^del.top()))
big.pop(),del.pop(); big.pop();
}
inline int size(void)
{
return big.size()-del.size();
}
}T[N],M[N],A;
class LCA_Solver
{
private:
static const int P=17;
int anc[N][P],dep[N];
inline void swap(int &x,int &y)
{
int t=x; x=y; y=t;
}
inline void reset(int now)
{
for (RI i=0;i<P-1;++i) if (anc[now][i])
anc[now][i+1]=anc[anc[now][i]][i]; else break;
}
inline int query(int x,int y)
{
RI i; if (dep[x]<dep[y]) swap(x,y); for (i=P-1;~i;--i)
if (dep[anc[x][i]]>=dep[y]) x=anc[x][i]; if (x==y) return x;
for (i=P-1;~i;--i) if (anc[x][i]!=anc[y][i])
x=anc[x][i],y=anc[y][i]; return anc[x][0];
}
public:
#define to e[i].to
inline void DFS(int now,int fa)
{
dep[now]=dep[fa]+1; reset(now);
for (RI i=head[now];i;i=e[i].nxt)
if (to!=fa) anc[to][0]=now,DFS(to,now);
}
#undef to
inline int dis(int x,int y)
{
return dep[x]+dep[y]-(dep[query(x,y)]<<1);
}
}L;
class Point_Division_Solver
{
private:
int size[N],tofa[N]; bool vis[N];
inline void maxer(int &x,int y)
{
if (y>x) x=y;
}
#define to e[i].to
inline void travel(int now,int fa,int fart)
{
T[rt].push(L.dis(now,fart)); for (RI i=head[now];i;i=e[i].nxt)
if (to!=fa&&!vis[to]) travel(to,now,fart);
}
public:
inline void getrt(int now,int fa)
{
size[now]=1; mx[now]=0; for (RI i=head[now];i;i=e[i].nxt)
if (to!=fa&&!vis[to]) getrt(to,now),size[now]+=size[to],maxer(mx[now],size[to]);
if (maxer(mx[now],sonsize-size[now]),mx[now]<mx[rt]) rt=now;
}
inline void solve(int now,int fa)
{
tofa[now]=fa; vis[now]=1; M[now].push(0); travel(now,fa,fa);
for (RI i=head[now];i;i=e[i].nxt) if (to!=fa&&!vis[to])
mx[rt=0]=INF,sonsize=size[to],getrt(to,now),to=rt,solve(rt,now),M[now].push(T[to].top());
A.push(M[now].get());
}
#undef to
inline void Off(int now)
{
M[now].push(0); if (M[now].size()==2) A.push(M[now].top());
for (RI i=now;tofa[i];i=tofa[i])
{
int fa=tofa[i],D=L.dis(fa,now),temp=T[i].top();
T[i].push(D); if (D<=temp) continue;
int mx=M[fa].get(),size=M[fa].size();
if (temp) M[fa].erase(temp); M[fa].push(D);
int Mx=M[fa].get(); if (Mx>mx)
{
if (size>=2) A.erase(mx);
if (M[fa].size()>=2) A.push(Mx);
}
}
}
inline void On(int now)
{
if (M[now].size()==2) A.erase(M[now].top()); M[now].erase(0);
for (RI i=now;tofa[i];i=tofa[i])
{
int fa=tofa[i],D=L.dis(fa,now),temp=T[i].top();
T[i].erase(D); if (D!=temp) continue;
int mx=M[fa].get(),size=M[fa].size();
M[fa].erase(D); if (temp=T[i].top()) M[fa].push(temp);
int Mx=M[fa].get(); if (Mx<mx)
{
if (size>=2) A.erase(mx);
if (M[fa].size()>=2) A.push(Mx);
}
}
}
}S;
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
RI i; for (F.read(n),num=n,i=1;i<n;++i) F.read(x),F.read(y),add(x,y),add(y,x);
for (sonsize=n,L.DFS(1,0),mx[rt=0]=INF,S.getrt(1,0),S.solve(rt,0),F.read(m),i=1;i<=m;++i)
{
F.get_alpha(opt); if (opt^'C') { F.write(num<=1?num-1:A.top()); continue; }
F.read(x); if (is_light[x]) S.Off(x),++num; else S.On(x),--num; is_light[x]^=1;
}
return F.Fend(),0;
}
Luogu P2056 [ZJOI2007]捉迷藏的更多相关文章
- 洛谷 P2056 [ZJOI2007]捉迷藏 解题报告
P2056 [ZJOI2007]捉迷藏 题目描述 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏.他们的家很大且构造很奇特,由\ ...
- 洛谷 P2056 [ZJOI2007]捉迷藏 题解【点分治】【堆】【图论】
动态点分治入 门 题? 题目描述 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏.他们的家很大且构造很奇特,由 \(N\) 个屋 ...
- 洛谷 P2056 [ZJOI2007]捉迷藏 || bzoj 1095: [ZJOI2007]Hide 捉迷藏 || 洛谷 P4115 Qtree4 || SP2666 QTREE4 - Query on a tree IV
意识到一点:在进行点分治时,每一个点都会作为某一级重心出现,且任意一点只作为重心恰好一次.因此原树上任意一个节点都会出现在点分树上,且是恰好一次 https://www.cnblogs.com/zzq ...
- P2056 [ZJOI2007]捉迷藏
传送门 如果没有修改显然就直接点分治 有修改那就动态点分治 动态点分治就是在点分树上维护一些东西,查询时也在点分树上查 因为点分树深度是$log$的所以可以保证时间复杂度 此题我们需要在点分树上维护 ...
- 并不对劲的bzoj1095:p2056:[ZJOI2007]捉迷藏
题目大意 给一\(n\)(\(n\leq10^5\))个点的一棵树,每个点有可能是黑色或白色,一开始所有点都是黑色,支持以下两种操作: 1.改变一个点的颜色 2.询问最远的黑色点对的距离 题解 据说是 ...
- [洛谷P2056][ZJOI2007]捉迷藏(2019-7-20考试)
题目大意:有一棵$n(n\leqslant10^6)$个点的树,上面所有点是黑点,有$m$次操作: $C\;u$:把点$u$颜色翻转 $G$:问树上最远的两个黑点的距离,若没有黑点输出$0$ 题解:有 ...
- 树上最长链 Farthest Nodes in a Tree LightOJ - 1094 && [ZJOI2007]捉迷藏 && 最长链
树上最远点对(树的直径) 做法1:树形dp 最长路一定是经过树上的某一个节点的. 因此: an1[i],an2[i]分别表示一个点向下的最长链和次长链,次长链不存在就设为0:这两者很容易求 an3[i ...
- [luogu P1169] [ZJOI2007]棋盘制作
[luogu P1169] [ZJOI2007]棋盘制作 题目描述 国际象棋是世界上最古老的博弈游戏之一,和中国的围棋.象棋以及日本的将棋同享盛名.据说国际象棋起源于易经的思想,棋盘是一个8*8大小的 ...
- 悬线法 || BZOJ 1057: [ZJOI2007]棋盘制作 || Luogu P1169 [ZJOI2007]棋盘制作
题面:P1169 [ZJOI2007]棋盘制作 题解: 基本是悬线法板子,只是建图判断时有一点点不同. 代码: #include<cstdio> #include<cstring&g ...
随机推荐
- Android为TV端助力 MediaPlayer的一些使用方法简历
这里提供一些MediaPlayer的简单方法,方便以后熟练的使用它! 1)如何获得MediaPlayer实例: 可以使用直接new的方式: MediaPlayer mp = new MediaPlay ...
- Testlink1.9.17使用方法(第七章 测试用例集管理)
第七章 测试用例集管理 QQ交流群:585499566 测试用例准备好以后,可以对测试用例集进行相关的操作. 一. 添加测试用例到测试计划中 在主页的“当前测试计划”下拉列表里-->选择一个测试 ...
- GlusterFS 安装 on centos7
本文演示如何在CentOS7上安装,配置和使用GlusterFS. 1 准备工作 1.1 基础设施 编号 IP OS 主机名 角色 说明 A 192.168.1.101 CentOS7.4 ddc_n ...
- LINQ的求和 平均 最大 最小 分组 计数 等等
1.简单形式: var q = from p in db.Products group p by p.CategoryID into g select g; 语句描述:使用Group By按Categ ...
- SQL Server基础之表级触发器
触发器分为两种,一种与数据表绑定,响应数据表指定动作(insert.delete或update),此处称为表级:一种与数据库本身绑定,响应数据定义语句(主要是CREATE.ALTER 和 DROP 开 ...
- LeetCode算法题-Two Sum II - Input array is sorted
这是悦乐书的第179次更新,第181篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第38题(顺位题号是167).给定已按升序排序的整数数组,找到两个数字,使它们相加到特定 ...
- python3编写网络爬虫21-scrapy框架的使用
一.scrapy框架的使用 前面我们讲了pyspider 它可以快速的完成爬虫的编写 不过pyspider也有一些缺点 例如可配置化不高 异常处理能力有限对于一些反爬虫程度非常强的网站 爬取显得力不从 ...
- spring简述
1. 什么是Spring Spring:SE/EE开发的一站式框架.(有EE开发每一层的解决方案) WEB层:SpringMVC Service层:Spring的Bean管理,Spring的声明式事务 ...
- Java 7 for Absolute Beginners/Java 7基础教程--读后感
1. 很适合部分初学者,因为书中对于JAVA许多特性都有涉猎,而且也是浅尝即止.如果读者没有受过专业的编程训练,那么在阅读此书时不会畏惧.因为书中没有充斥着大量的计算机理论知识,虽然作者依然覆盖了JA ...
- Java面试知识点之计算机网络篇(一)
前言:在Java面试中,计算机网络的知识也是一项重点,因此笔者在此对计算机网络的相关知识进行总结. 1.OSI参考模型 自下而上:物理层(物理介质,比特流).数据链路层(网卡.交换机).网络层(IP协 ...