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 ...
随机推荐
- Python 多线程并发程序设计与分析
多线程并发程序设计与分析 by:授客 QQ:1033553122 1.技术难点分析与总结 难点1:线程运行时,运行顺序不固定 难点2:同一段代码,再不加锁的情况下,可能被多个线程同时执行,这会造成很多 ...
- Android 程序崩溃之后fragment出现画面重叠问题
1.解决方法: 直接在包含Fragment的Activity中复写onSaveInstanceState()方法,使其空实现 @Override protected void onSaveInstan ...
- C# 利用Log4Net进行日志记录
概述 本文主要简单说明如何使用Log4Net进行日志记录,在程序开发过程中记录日志的优点: 它可以提供应用程序运行时的精确环境,可供开发人员尽快找到应用程序中的Bug: 一旦在程序中加入了Log 输出 ...
- Android中Ijkplayer最简单的使用
先添加依赖: compile 'com.dou361.ijkplayer:jjdxm-ijkplayer:1.0.5' MainActivity里面: public class MainActivit ...
- sdk manager闪退
,1确认好sdk环境变量是否都正确 命令行:android 来验证 2确定jdk是否安装正确 命令行:java 和 javac 都没有问题,就将sdk安装版覆盖安装下不要卸载,不然下载的太慢了 ...
- Android内存优化(一)Dalvik虚拟机和ART虚拟机对比
1.概述 Android4.4以上开始使用ART虚拟机,在此之前我们一直使用的Dalvik虚拟机,那么为什么Google突然换了Android运行的虚拟机呢?答案只有一个:ART虚拟机更优秀. 2.D ...
- java I/O工作机制
java I/O 的基本架构: 1:基于字节操作的I/O接口 InputStream OutputStream 2:基于字符操作的I/O接口 Writer 和Reader 3:基于磁盘操作的I/O接口 ...
- (网页)jQueryAJAXtimeout超时问题详解(转)
先给大家分析下超时原因: 1.网络不通畅. 2.后台运行比较慢(服务器第一次运行时,容易出现) 超时结果:JQ中 timeout设置请求超时时间. 如果服务器响应时间超过了 设置的时间,则进入 ERR ...
- Percona XtraBackup 安装介绍篇
XtraBackup介绍 XtraBackup是Percona公司的开源项目,用以实现类似Innodb官方的热备份工具InnoDB Hot Backup的功能,它支持在线热备份(备份时不影响数据读写) ...
- [20181116]18c DML 日志优化.txt
[20181116]18c DML 日志优化.txt 1.环境:xxxxxxxx> select banner_full from v$version;BANNER_FULL---------- ...