入坑动态点分治的题目,感觉还不错被卡常后重构代码

首先静态点分治相信大家肯定都会,就是不断找重心然后暴力计算每棵子树内的贡献。

这题如果只有单次询问,我们很容易想到对于每个分治中心的所以儿子的子树中找两条最长链拼起来。

或者是直接以这个点为端点的一条链的最大值。

如果就这么做复杂度将达到\(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]捉迷藏的更多相关文章

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

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

  2. 洛谷 P2056 [ZJOI2007]捉迷藏 题解【点分治】【堆】【图论】

    动态点分治入 门 题? 题目描述 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏.他们的家很大且构造很奇特,由 \(N\) 个屋 ...

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

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

  4. P2056 [ZJOI2007]捉迷藏

    传送门 如果没有修改显然就直接点分治 有修改那就动态点分治 动态点分治就是在点分树上维护一些东西,查询时也在点分树上查 因为点分树深度是$log$的所以可以保证时间复杂度 此题我们需要在点分树上维护 ...

  5. 并不对劲的bzoj1095:p2056:[ZJOI2007]捉迷藏

    题目大意 给一\(n\)(\(n\leq10^5\))个点的一棵树,每个点有可能是黑色或白色,一开始所有点都是黑色,支持以下两种操作: 1.改变一个点的颜色 2.询问最远的黑色点对的距离 题解 据说是 ...

  6. [洛谷P2056][ZJOI2007]捉迷藏(2019-7-20考试)

    题目大意:有一棵$n(n\leqslant10^6)$个点的树,上面所有点是黑点,有$m$次操作: $C\;u$:把点$u$颜色翻转 $G$:问树上最远的两个黑点的距离,若没有黑点输出$0$ 题解:有 ...

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

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

  8. [luogu P1169] [ZJOI2007]棋盘制作

    [luogu P1169] [ZJOI2007]棋盘制作 题目描述 国际象棋是世界上最古老的博弈游戏之一,和中国的围棋.象棋以及日本的将棋同享盛名.据说国际象棋起源于易经的思想,棋盘是一个8*8大小的 ...

  9. 悬线法 || BZOJ 1057: [ZJOI2007]棋盘制作 || Luogu P1169 [ZJOI2007]棋盘制作

    题面:P1169 [ZJOI2007]棋盘制作 题解: 基本是悬线法板子,只是建图判断时有一点点不同. 代码: #include<cstdio> #include<cstring&g ...

随机推荐

  1. Java map 详解

    Map 提供了一个更通用的元素存储方法.Map 集合类用于存储元素对(称作“键”和“值”),其中每个键映射到一个值. 初始化一个集合:  Map<String, String> map = ...

  2. HDU 2086 A1 = ? (找规律推导公式 + 水题)(Java版)

    Equations 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2086 ——每天在线,欢迎留言谈论. 题目大意: 有如下方程:Ai = (Ai-1 ...

  3. mysql的数据类型和字段属性

    本文内容: 数据类型 数值类型 整数型 浮点型 定点型 日期时间类型 字符串类型 补充: 显示宽度与zerofll 记录长度 字段属性 空\不为空值:NULL.NOT NULL 主键:primary ...

  4. mssql sqlserver 关键字 GROUPING用法简介及说明

    转自: http://www.maomao365.com/?p=6208  摘要: GROUPING 用于区分列是否由 ROLLUP.CUBE 或 GROUPING SETS 聚合而产生的行 如果是原 ...

  5. 前后端分离djangorestframework——restful规范

    restful现在非常流行,所以很有必要提一下 web服务交互 在浏览器中能看到的每个网站,都是一个web服务.那么我们在提供每个web服务的时候,都需要前后端交互,前后端交互就一定有一些实现方案,我 ...

  6. Java中输入字符的排列以及按从小到大的顺序输出

    今天笔试,遇到一个问题,大意就是输入一行字符,例如a b c ,按从小到大的顺序输出它们排列而成的字符串,输出就是abc acb bac bca cba cab.求这个程序怎么实现. 其实这个题很简单 ...

  7. LeetCode算法题-Sum of Left Leaves(Java实现)

    这是悦乐书的第217次更新,第230篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第85题(顺位题号是404).找到给定二叉树中所有左叶的总和.例如: 二叉树中有两个左叶 ...

  8. python3编写网络爬虫23-分布式爬虫

    一.分布式爬虫 前面我们了解Scrapy爬虫框架的基本用法 这些框架都是在同一台主机运行的 爬取效率有限 如果多台主机协同爬取 爬取效率必然成倍增长这就是分布式爬虫的优势 1. 分布式爬虫基本原理 1 ...

  9. 序列对象(bytearray, bytes,list, str, tuple)

    列表: L.append(x) # x追加到L尾部 L.count(x) # 返回x在L中出现的次数 L.extend(m) # Iterable m的项追加到L末尾 L += m # 功能同L.ex ...

  10. 【PS技巧】如何拼图

    1.材料准备 根据对图片的内容表达,粗略的“计划”,每张图片摆放位置及尺寸.C与D等高,C/D与B叠高后与A等高.C与D叠宽后与B等宽. 2.记录每张图片原始大小 (1)双击工作区,打开待拼接图片 ( ...