[ZJOI2007] 捉迷藏

时间:2022-08-28 20:36:00

idea1

可能会死掉的想法:考虑点分治维护每个分治中心x到达分治块内的个点距离,具体是用堆维护分治快内的x的儿子y到y的子树内的所有点距离(记为C[y]),取所有C[y]的top+e(x,y)放入x的堆里(记为B[x]),答案为所有B[x]的top+top2的最大值,可以在用一个堆维护(记为A)。

参考的实现,可以得到80分。

#include <queue>
#include <cstdio>
#include <cassert>
#include <algorithm>
#include <iostream>
using namespace std; const int N=1e5+10; struct ErasableHeap {
std::priority_queue<int> A,B;
void insert(int w) {A.push(w);}
void erase(int w) {B.push(w);}
int size() {return A.size()-B.size();}
int top() {
while(B.size()&&A.top()==B.top()) A.pop(),B.pop();
return A.top();
}
int top2() {
int top1=top(); erase(top1);
int topt=top(); insert(top1);
return topt;
}
} A,B[N],C[N]; struct Edge {
int to,len,last;
} e[N<<1]; int n,q,val[N],tar[N];
int head[N]; void addEdge(int x,int y,int w) {
static int cnt=1;
e[++cnt]=(Edge){y,w,head[x]},head[x]=cnt;
e[++cnt]=(Edge){x,w,head[y]},head[y]=cnt;
} namespace dbl {
int fa[N][20],ds[N][20],dep[N];
void pre(int x,int pa) {
dep[x]=dep[fa[x][0]=pa]+1;
for(int i=1; (1<<i)<=dep[x]; ++i) {
fa[x][i]=fa[fa[x][i-1]][i-1];
ds[x][i]=ds[x][i-1]+ds[fa[x][i-1]][i-1];
}
for(int i=head[x]; i; i=e[i].last) {
if(e[i].to==pa) continue;
ds[e[i].to][0]=e[i].len;
pre(e[i].to,x);
}
}
int getDis(int x,int y) {
if(dep[x]<dep[y]) std::swap(x,y);
int dif=dep[x]-dep[y],sum=0;
for(int i=19; ~i; --i)
if((dif>>i)&1) sum+=ds[x][i],x=fa[x][i];
if(x==y) return sum;
for(int i=19; ~i; --i) {
if(fa[x][i]!=fa[y][i]) {
sum+=ds[x][i],x=fa[x][i];
sum+=ds[y][i],y=fa[y][i];
}
}
return sum+ds[x][0]+ds[y][0];
}
} void tryInsB(int x,int dis) {
if(B[x].size()==0) {
if(!val[x]) A.insert(dis);
B[x].insert(dis);
} else if(B[x].size()==1) {
if(!val[x]&&B[x].top()<dis) A.erase(B[x].top());
B[x].insert(dis);
A.insert(B[x].top()+B[x].top2());
} else if(B[x].top2()<dis) {
A.erase(B[x].top()+B[x].top2());
B[x].insert(dis);
A.insert(B[x].top()+B[x].top2());
} else B[x].insert(dis);
}
void tryEraB(int x,int dis) {
if(!B[x].size()) return; // notice
if(B[x].size()==1) {
if(!val[x]) A.erase(dis);
B[x].erase(dis);
} else if(B[x].top2()<=dis) {
A.erase(B[x].top()+B[x].top2());
B[x].erase(dis);
if(B[x].size()>1) A.insert(B[x].top()+B[x].top2());
else if(!val[x]) A.insert(B[x].top());
} else B[x].erase(dis);
} int stk[N],top;
void whiteToBlack(int d) {
for(int i=tar[d]; i; i=tar[e[i^1].to]) stk[++top]=i;
for(int&i=top; i>=1; --i) {
int x=e[stk[i]^1].to;
int y=e[stk[i]].to,dis=dbl::getDis(d,y);
if(C[y].top()>dis) C[y].erase(dis);
else if(C[y].size()>1&&C[y].top2()==dis) C[y].erase(dis);
else {
tryEraB(x,dis+e[stk[i]].len); C[y].erase(dis);
if(C[y].size()) tryInsB(x,C[y].top()+e[stk[i]].len);
}
}
if(B[d].size()==1) A.erase(B[d].top());
val[d]=1;
}
void blackToWhite(int d) {
for(int i=tar[d]; i; i=tar[e[i^1].to]) stk[++top]=i;
for(int&i=top; i>=1; --i) {
int x=e[stk[i]^1].to;
int y=e[stk[i]].to,dis=dbl::getDis(d,y);
if(C[y].size()==0) C[y].insert(dis),tryInsB(x,dis+e[stk[i]].len);
else if(C[y].top()>=dis) C[y].insert(dis);
else {
tryEraB(x,C[y].top()+e[stk[i]].len); C[y].insert(dis);
tryInsB(x,dis+e[stk[i]].len);
}
}
if(B[d].size()==1) A.insert(B[d].top());
val[d]=0;
} int rt,sum,siz[N];
bool ban[N];
void getRoot(int x,int fa) {
static int f[N]={N+N};
f[x]=0,siz[x]=1;
for(int i=head[x]; i; i=e[i].last) {
if(e[i].to==fa||ban[e[i].to]) continue;
getRoot(e[i].to,x);
siz[x]+=siz[e[i].to];
f[x]=std::max(f[x],siz[e[i].to]);
}
f[x]=std::max(f[x],sum-siz[x]);
if(f[x]<f[rt]) rt=x;
}
void getC(int x,int fa,int dis,int top) {
C[top].insert(dis);
for(int i=head[x]; i; i=e[i].last) {
if(e[i].to==fa||ban[e[i].to]) continue;
getC(e[i].to,x,dis+e[i].len,top);
}
}
void build(int x) {
ban[x]=true;
for(int i=head[x]; i; i=e[i].last) {
if(ban[e[i].to]) continue;
getC(e[i].to,x,0,e[i].to);
B[x].insert(C[e[i].to].top()+e[i].len);
}
for(int i=head[x]; i; i=e[i].last) {
if(ban[e[i].to]) continue;
rt=0;
sum=siz[e[i].to];
getRoot(e[i].to,x);
tar[rt]=i;
build(rt);
}
if(B[x].size()>1) A.insert(B[x].top()+B[x].top2());
else if(B[x].size()) A.insert(B[x].top());
} int main() {
scanf("%d",&n);
for(int i=n,x,y; --i; ) {
scanf("%d%d",&x,&y);
addEdge(x,y,1);
}
dbl::pre(1,0);
sum=n;
getRoot(1,0);
build(rt);
int white=n,q,x;
char chr[5];
scanf("%d",&q);
while(q--) {
scanf("%s",chr);
if(*chr=='G') {
if(!white) puts("-1");
else if(white==1) puts("0");
else printf("%d\n",std::max(0,A.top()));
} else {
scanf("%d",&x);
if(!val[x]) --white,whiteToBlack(x);
else ++white,blackToWhite(x);
}
}
return 0;
}

代码中的一个技巧:记录tar[x]表示上一级分治中心连向x所在子树的边的编号,这样就能在爬树时同时得到原树儿子(e[tar[i]].to)、边长(e[tar[i]].len)、和上一层的分治中心(e[tar[i]^1].to)。

但想法有一个缺陷:注意notice的那句,为什么会有这种情况出现?原来对于同一个y对应的x可能不唯一,且每种对应的意义不一样(因为对应x不同,分治块范围不同)。补救措施可以考虑建立分治树时将被对应多次的y拆开(拆开的点都对应原树上的‘y’节点),最坏情况下单点y会被拆成deg[y]个,但是处于极限状况的点个数不会太多,总点数仍是O(n)级别。

经过抢救的部分 90分弃坑了

int yCnt,yBel[N],myY[N],myX[N],myL[N];

void whiteToBlack(int d) {
for(int i=d; myX[i]; i=myX[i]) {
int x=myX[i];
int y=myY[i],dis=dbl::getDis(d,yBel[y]);
if(C[y].top()>dis) C[y].erase(dis);
else if(C[y].size()>1&&C[y].top2()==dis) C[y].erase(dis);
else {
tryEraB(x,dis+myL[i]); C[y].erase(dis);
if(C[y].size()) tryInsB(x,C[y].top()+myL[i]);
}
}
if(B[d].size()==1) A.erase(B[d].top());
val[d]=1;
}
void blackToWhite(int d) {
for(int i=d; myX[i]; i=myX[i]) {
int x=myX[i];
int y=myY[i],dis=dbl::getDis(d,yBel[y]);
if(C[y].size()==0) C[y].insert(dis),tryInsB(x,dis+myL[i]);
else if(C[y].top()>=dis) C[y].insert(dis);
else {
tryEraB(x,C[y].top()+myL[i]); C[y].insert(dis);
tryInsB(x,dis+myL[i]);
}
}
if(B[d].size()==1) A.insert(B[d].top());
val[d]=0;
} int rt,sum,siz[N];
bool ban[N];
void getRoot(int x,int fa) {
static int f[N]={N+N};
f[x]=0,siz[x]=1;
for(int i=head[x]; i; i=e[i].last) {
if(e[i].to==fa||ban[e[i].to]) continue;
getRoot(e[i].to,x);
siz[x]+=siz[e[i].to];
f[x]=std::max(f[x],siz[e[i].to]);
}
f[x]=std::max(f[x],sum-siz[x]);
if(f[x]<f[rt]) rt=x;
}
void getC(int x,int fa,int dis,int top) {
C[top].insert(dis);
for(int i=head[x]; i; i=e[i].last) {
if(e[i].to==fa||ban[e[i].to]) continue;
getC(e[i].to,x,dis+e[i].len,top);
}
}
void build(int x) {
ban[x]=true;
for(int i=head[x]; i; i=e[i].last) {
if(ban[e[i].to]) continue; yBel[++yCnt]=e[i].to;
getC(e[i].to,x,0,yCnt);
B[x].insert(C[yCnt].top()+e[i].len); rt=0;
sum=siz[e[i].to];
getRoot(e[i].to,x);
myX[rt]=x;
myY[rt]=yCnt;
myL[rt]=e[i].len;
build(rt);
}
if(B[x].size()>1) A.insert(B[x].top()+B[x].top2());
else if(B[x].size()) A.insert(B[x].top());
}

idea2

别人的不会死掉的想法:直接用C[y]维护在x的分治块内y的子树中的所有节点到x的距离,B[x]取所有C[y]的最大值,由于不在依赖原树上的父子关系,避免了一个y对应了多个x的情况。

总结

我看别人题解的时候就不该看一半

[ZJOI2007] 捉迷藏的更多相关文章

  1. 洛谷 P2056 &lbrack;ZJOI2007&rsqb;捉迷藏 解题报告

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

  2. 树上最长链 Farthest Nodes in a Tree LightOJ - 1094 &amp&semi;&amp&semi; &lbrack;ZJOI2007&rsqb;捉迷藏 &amp&semi;&amp&semi; 最长链

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

  3. 洛谷 P2056 &lbrack;ZJOI2007&rsqb;捉迷藏 题解【点分治】【堆】【图论】

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

  4. 洛谷 P2056 &lbrack;ZJOI2007&rsqb;捉迷藏 &vert;&vert; bzoj 1095&colon; &lbrack;ZJOI2007&rsqb;Hide 捉迷藏 &vert;&vert; 洛谷 P4115 Qtree4 &vert;&vert; SP2666 QTREE4 - Query on a tree IV

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

  5. &lbrack;ZJOI2007&rsqb;捉迷藏(动态点分治&sol;(括号序列)(线段树))

    题目描述 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条双向走廊组成,这N-1条 ...

  6. Luogu P2056 &lbrack;ZJOI2007&rsqb;捉迷藏

    入坑动态点分治的题目,感觉还不错被卡常后重构代码 首先静态点分治相信大家肯定都会,就是不断找重心然后暴力计算每棵子树内的贡献. 这题如果只有单次询问,我们很容易想到对于每个分治中心的所以儿子的子树中找 ...

  7. BZOJ&period;1095&period;&lbrack;ZJOI2007&rsqb;捉迷藏&lpar;线段树 括号序列&rpar;

    BZOJ 洛谷 对树DFS得到括号序列.比如这样一个括号序列:[A[B[E][F[H][I]]][C][D[G]]]. 那比如\(D,E\)间的最短距离,就是将\(D,E\)间的括号序列取出:][[] ...

  8. 【bzoj1095】 ZJOI2007—捉迷藏

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

  9. P2056 &lbrack;ZJOI2007&rsqb;捉迷藏

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

随机推荐

  1. CSS自适应布局&lpar;包括两边宽度固定中间宽度自适应与中间宽度固定两边宽度自适应&rpar;

    1.两边宽度固定,中间宽度自适应 (1)非CSS3布局,浮动定位都可以(以下用浮动) css样式: #left { float: left;width: 200px; background: lime ...

  2. 20145222黄亚奇《Java程序设计》第5周学习总结

    教材学习内容总结 Java中所有错误都会被打包为对象,运用try.catch,可以在错误发生时显示友好的错误信息. 运用try.catch,还可以在捕捉处理错误之后,尝试恢复程序正常执行流程.如: i ...

  3. Spring4&period;0学习笔记&lpar;11&rpar; —— Spring AspectJ 的五种通知

    Spring AspectJ 一.基于注解的方式配置通知 1.额外引入的jar包: a) com.springsource.org.aopalliance-1.0.0.jar b) com.sprin ...

  4. C语言复杂的函数指针声明

    复习C语言ING,发现复杂的函数指针声明看不懂,百度半天终于略知一二. 讲的比较详细的一篇blog: http://blog.csdn.net/megaboy/article/details/4827 ...

  5. FatMouse&&num;39&semi;s Speed 基础DP

    FatMouse's Speed Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) ...

  6. C语言:min和max头文件

    转自:http://www.cppblog.com/jince/archive/2010/09/14/126600.html min和max头文件 虽然说求最大值最小值函数在哪个头文件下并不是非常重要 ...

  7. RecyclerView分割线——万能分割线

    参照网络上众多的分割线设计方法,对方法进行调整和修改,最终完成的比较通用的RecyclerView分割线,底部会附上参考网址,大家可以去看一下. 在正文之前,先说一下个人看法:研究下来,我发现,其实最 ...

  8. JHipster技术栈定制 - JHipster Registry消息总线配置

    本文说明了如何定制化JHipster-Registry,增加消息总线功能. 实现的效果就是修改配置中心的文件后,通过消息队列主动推送给微服务而无需重启微服务,实现配置内容热加载. 1 整体规划 1.1 ...

  9. Navicat Premium 12破解方法

    来源网址:https://www.jianshu.com/p/42a33b0dda9c 1.按步骤安装Navicat Premium,如果没有可以去官网下载:http://www.navicat.co ...

  10. HDU-1686-KMP-水题

    纯KMP #include <cstdio> #include <algorithm> #include <cstring> #include <ctype. ...