【BZOJ1095】捉迷藏,动态点分治
2016-12-15 10:37
393 查看
传送门
题意
给定一棵树,树上的点是黑点或白点,修改一个点的颜色或查询树上两个最远黑点的距离
原本以为动态点分治是个什么很高级的东西
原来不是像LCT一样恶心的东西啊,但也很恶心了
问了问别人才知道所谓“动态点分治”只是把点分治时得到的信息存下来,用数据结构维护一下就可以了
如果知道了什么是动态点分治,这个题目用它来做的思路还是好YY的
对于每次找到的重心x,它的子树集是{Vi},那么对于每一个Vi建一个堆,来维护其中的黑点及其深度,我们称x的子树Vi所代表的堆为Q1x−i
特殊的,把x自身也建一个堆出来Q1x−x
每一个Q1堆中的元素最多是n个,∑|Q1|大概是O(nlogn)
(我原本以为这样的堆最多大概有nlogn个,但在测试样例时发现是O(n)数量级的,仔细想了想发现的确如此,虽然对程序没有太大影响,但这很不应该)
然后再对于每一个x建一个堆Q2x={max{Q1x−i}}
∑|Q2|大概是O(n)
再在全局搞一个堆Q3={max{Q2x}+secondlarge{Q2x}|x=1..n}
查询时取Q3的max就可以了
修改时比较麻烦
在分治过程中每个点被遍历logn次,所以要修改logn个Q1
具体过程可以描述为
要修改的点是i,i是子树集v里的点,现在要求改重心为x时的情况
先在Q3中删掉max{Q2x}+secondlarge{Q2x}
再在Q2v中删去max{Q1x−v}
往Q1x−v添加/删除i的信息
再在Q2v中加入max{Q1x−v}
最后在Q3中加入max{Q2x}+secondlarge{Q2x}
mrazer:“删除操作可以再开一个辅助堆,如果辅助堆的堆顶和真正堆的堆顶相等,就都弹出来,继续找下去”
UPD
对于Q1堆,Yveh大爷提供了一种更容易理解的想法
实际上可以把每次寻找到的重心合起来看做一棵树,就是父重心和子重心的关系
或者称之为重心重构树
对于每个重心的所有Q1堆,实际上存的是重构树中它所有子树到它本身的信息
复杂度O(mlog2n)
本来写起来就很麻烦了……
我又用了pair啥的……
然后在BZ上就MLE了,在cogs上就RE了
考虑到这样做会使辅助堆中加入很多元素但堆顶弹出来的会很少,导致M
所以一怒之下改成set
在BZ上跑了27s,在cogs上T了最后一个点(每个时限10s)
用尽浑身解数来卡常,算了一下要跑13~14s左右……
看了Po姐的博客发现其实不用pair,直接在堆中存距离就可以了
因为这些距离相等的点是可以看成等效的……
有时间再去想想神奇的括号序列做法和LCT做法
代码写得不可看
Code
set+pair
Code
堆
题意
给定一棵树,树上的点是黑点或白点,修改一个点的颜色或查询树上两个最远黑点的距离
原本以为动态点分治是个什么很高级的东西
原来不是像LCT一样恶心的东西啊,但也很恶心了
问了问别人才知道所谓“动态点分治”只是把点分治时得到的信息存下来,用数据结构维护一下就可以了
如果知道了什么是动态点分治,这个题目用它来做的思路还是好YY的
对于每次找到的重心x,它的子树集是{Vi},那么对于每一个Vi建一个堆,来维护其中的黑点及其深度,我们称x的子树Vi所代表的堆为Q1x−i
特殊的,把x自身也建一个堆出来Q1x−x
每一个Q1堆中的元素最多是n个,∑|Q1|大概是O(nlogn)
(我原本以为这样的堆最多大概有nlogn个,但在测试样例时发现是O(n)数量级的,仔细想了想发现的确如此,虽然对程序没有太大影响,但这很不应该)
然后再对于每一个x建一个堆Q2x={max{Q1x−i}}
∑|Q2|大概是O(n)
再在全局搞一个堆Q3={max{Q2x}+secondlarge{Q2x}|x=1..n}
查询时取Q3的max就可以了
修改时比较麻烦
在分治过程中每个点被遍历logn次,所以要修改logn个Q1
具体过程可以描述为
要修改的点是i,i是子树集v里的点,现在要求改重心为x时的情况
先在Q3中删掉max{Q2x}+secondlarge{Q2x}
再在Q2v中删去max{Q1x−v}
往Q1x−v添加/删除i的信息
再在Q2v中加入max{Q1x−v}
最后在Q3中加入max{Q2x}+secondlarge{Q2x}
mrazer:“删除操作可以再开一个辅助堆,如果辅助堆的堆顶和真正堆的堆顶相等,就都弹出来,继续找下去”
UPD
对于Q1堆,Yveh大爷提供了一种更容易理解的想法
实际上可以把每次寻找到的重心合起来看做一棵树,就是父重心和子重心的关系
或者称之为重心重构树
对于每个重心的所有Q1堆,实际上存的是重构树中它所有子树到它本身的信息
复杂度O(mlog2n)
本来写起来就很麻烦了……
我又用了pair啥的……
然后在BZ上就MLE了,在cogs上就RE了
考虑到这样做会使辅助堆中加入很多元素但堆顶弹出来的会很少,导致M
所以一怒之下改成set
在BZ上跑了27s,在cogs上T了最后一个点(每个时限10s)
用尽浑身解数来卡常,算了一下要跑13~14s左右……
看了Po姐的博客发现其实不用pair,直接在堆中存距离就可以了
因为这些距离相等的点是可以看成等效的……
有时间再去想想神奇的括号序列做法和LCT做法
代码写得不可看
Code
set+pair
#include<cstdio> #include<iostream> #include<vector> #include<set> #include <cstdlib> #define M 100005 using namespace std; int n,m,G,tot,cnt; int siz[M],mx[M],tmp[M],owner[M*15],first[M]; typedef pair<int,int>node; vector<node>belong[M]; struct Node { int poi,fa,dis; }q[M]; struct edge{ int v,next; }e[M<<1]; set<node> Q1[M*15],Q2[M],Q3; set<node>::iterator it; bool vis[M],tp[M]; char *cp=(char *)malloc(20000000); inline void in(int &x) { for (;*cp<'0'||*cp>'9';++cp); for (x=0;*cp>='0'&&*cp<='9';++cp) x=x*10+(*cp)-48; } inline void out(int x) { if (!x) return; out(x/10); putchar(x%10+48); } inline void add(int x,int y) { e[++tot].v=y;e[tot].next=first[x];first[x]=tot; e[++tot].v=x;e[tot].next=first[y];first[y]=tot; } void find(int x,int fa) { siz[x]=1; mx[x]=0; tmp[++tmp[0]]=x; for (int i=first[x];i;i=e[i].next) if (!vis[e[i].v]&&e[i].v!=fa) find(e[i].v,x), siz[x]+=siz[e[i].v], mx[x]=max(mx[x],siz[e[i].v]); } void solve(int x) { tmp[0]=G=0; find(x,0); int i,z,now,head,tail,maxn; for (i=1;i<=tmp[0];++i) z=tmp[i], mx[z]=max(tmp[0]-siz[z],mx[z]), G=(!G||mx[G]>mx[z]?z:G); vis[G]=1; for (i=first[G];i;i=e[i].next) if (!vis[e[i].v]) { owner[++cnt]=G; now=e[i].v,head=1,tail=1,maxn=1; q[1]=(Node){e[i].v,0,1}; for (;head<=tail;++head) { x=q[head].poi; belong[x].push_back(make_pair(q[head].dis,cnt)); Q1[cnt].insert(make_pair(q[head].dis,x)); for (int j=first[x];j;j=e[j].next) if (e[j].v!=q[head].fa&&!vis[e[j].v]) q[++tail]=(Node){e[j].v,x,q[head].dis+1}; } if (!Q1[cnt].empty()) it=Q1[cnt].end(), --it, Q2[G].insert(make_pair((*it).first,cnt)); } owner[++cnt]=G; belong[G].push_back(make_pair(0,cnt)); Q1[cnt].insert(make_pair(0,G)); it=Q1[cnt].end(), --it; Q2[G].insert(make_pair((*it).first,cnt)); node a,b; if (!Q2[G].empty()) { it=Q2[G].end(); --it; a=*it; if (it!=Q2[G].begin()) b=*(--it), Q3.insert(make_pair(a.first+b.first,G)); } x=G; for (i=first[x];i;i=e[i].next) if (!vis[e[i].v]) solve(e[i].v); } main() { fread(cp,1,20000000,stdin); in(n); int v,t,x,i,y; for (i=1;i<n;++i) in(x),in(y), add(x,y); tot=n; solve(1); node a,b; for (in(m);m;--m) { ++cp; while (*cp!='G'&&*cp!='C') ++cp; if (*cp=='G') { if (!tot) puts("-1"); if (tot==1) puts("0"); if (tot>1) it=Q3.end(), --it, out((*it).first), putchar('\n'); } else { in(x); tp[x]^=1; tot+=(tp[x]?-1:1); for (i=0;i<belong[x].size();++i) { v=belong[x][i].second; t=owner[v]; if (!Q2[t].empty()) { it=Q2[t].end(); --it; a=*it; if (it!=Q2[t].begin()) b=*(--it), Q3.erase(make_pair(a.first+b.first,t)); } if (!Q1[v].empty()) it=Q1[v].end(), --it, Q2[t].erase(make_pair((*it).first,v)); if (tp[x]) Q1[v].erase(make_pair(belong[x][i].first,x)); else Q1[v].insert(make_pair(belong[x][i].first,x)); if (!Q1[v].empty()) it=Q1[v].end(), --it, Q2[t].insert(make_pair((*it).first,v)); if (!Q2[t].empty()) { it=Q2[t].end(); --it; a=*it; if (it!=Q2[t].begin()) b=*(--it), Q3.insert(make_pair(a.first+b.first,t)); } } } } }
Code
堆
#include<cstdio> #include<iostream> #include<vector> #include<queue> #include <cstdlib> #define M 100005 using namespace std; int n,G,tot,cnt; int siz[M],mx[M],tmp[M],owner[M*2]; vector<int> e[M]; typedef pair<int,int>node; vector<node>belong[M]; struct Node { int poi,fa,dis; }q[M]; priority_queue<int> Q1[2][M*2],Q2[2][M*2],Q3[2]; //Q1存子树节点及距离 Q2存Q1编号及距离 Q3存各个重心及距离 bool vis[M],tp[M]; int in() { char ch=getchar();int t=0; while (ch<'0'||ch>'9') ch=getchar(); while (ch>='0'&&ch<='9') t=t*10+ch-48,ch=getchar(); return t; } void find(int x,int fa) { siz[x]=1; mx[x]=0; tmp[++tmp[0]]=x; for (int v,i=0;i<e[x].size();++i) { v=e[x][i]; if (vis[v]||v==fa) continue; find(v,x); siz[x]+=siz[v]; mx[x]=max(mx[x],siz[v]); } } void solve(int x) { tmp[0]=G=0; find(x,0); for (int i=1;i<=tmp[0];++i) mx[tmp[i]]=max(tmp[0]-siz[tmp[i]],mx[tmp[i]]), G=(!G||mx[G]>mx[tmp[i]]?tmp[i]:G); vis[G]=1; for (int v,i=0;i<e[G].size();++i) { v=e[G][i]; if (vis[v]) continue; owner[++cnt]=G; int now=v,head=1,tail=1,mx=1; q[1]=(Node){v,0,1}; for (;head<=tail;++head) { v=q[head].poi; belong[v].push_back(make_pair(q[head].dis,cnt)); Q1[0][cnt].push(q[head].dis); for (int j=0;j<e[v].size();++j) if (e[v][j]!=q[head].fa&&!vis[e[v][j]]) q[++tail]=(Node){e[v][j],v,q[head].dis+1}; } if (!Q1[0][cnt].empty()) Q2[0][G].push(Q1[0][cnt].top()); } owner[++cnt]=G; belong[G].push_back(make_pair(0,cnt)); Q1[0][cnt].push(0); Q2[0][G].push(Q1[0][cnt].top()); int a,b; if (!Q2[0][G].empty()) { a=Q2[0][G].top(); Q2[0][G].pop(); if (!Q2[0][G].empty()) b=Q2[0][G].top(), Q3[0].push(a+b); Q2[0][G].push(a); } x=G; for (int v,i=0;i<e[x].size();++i) { v=e[x][i]; if (vis[v]) continue; solve(v); } } void Q1_clr(int v) { while (!Q1[0][v].empty()&&!Q1[1][v].empty()&&Q1[0][v].top()==Q1[1][v].top()) Q1[0][v].pop(),Q1[1][v].pop(); } void Q2_clr(int v) { while (!Q2[0][v].empty()&&!Q2[1][v].empty()&&Q2[0][v].top()==Q2[1][v].top()) Q2[0][v].pop(),Q2[1][v].pop(); } void Q3_clr() { while (!Q3[1].empty()&&!Q3[0].empty()&&Q3[1].top()==Q3[0].top()) Q3[0].pop(),Q3[1].pop(); } main() { n=in();tot=n; for (int x,y,i=1;i<n;++i) x=in(),y=in(), e[x].push_back(y), e[y].push_back(x); solve(1); for (int m=in();m;--m) { char ch=getchar(); while (ch!='G'&&ch!='C') ch=getchar(); if (ch=='G') { if (!tot) puts("-1"); if (tot==1) puts("0"); if (tot>1) Q3_clr(), printf("%d\n",Q3[0].top()); } else { int x=in(); tp[x]^=1; if (tp[x]) --tot; else ++tot; for (int v,t,i=0;i<belong[x].size();++i) { v=belong[x][i].second; t=owner[v]; Q2_clr(t); if (!Q2[0][t].empty()) { int a,b; a=Q2[0][t].top(); Q2[0][t].pop(); Q2_clr(t); if (!Q2[0][t].empty()) b=Q2[0][t].top(), Q3[1].push(a+b), Q3_clr(); Q2[0][t].push(a); } Q1_clr(v); if (!Q1[0][v].empty()) { Q2[1][t].push(Q1[0][v].top()); Q2_clr(t); } Q1[tp[x]][v].push(belong[x][i].first); Q1_clr(v); if (!Q1[0][v].empty()) { Q2[0][t].push(Q1[0][v].top()); Q2_clr(t); } Q2_clr(t); if (!Q2[0][t].empty()) { int a,b; a=Q2[0][t].top(); Q2[0][t].pop(); Q2_clr(t); if (!Q2[0][t].empty()) b=Q2[0][t].top(), Q3[0].push(a+b), Q3_clr(); Q2[0][t].push(a); } } } } }
相关文章推荐
- BZOJ 1095 Hide 捉迷藏 详解(动态点分治 堆维护)
- BZOJ1095 [ZJOI2007]Hide 捉迷藏 【动态点分治 + 堆】
- bzoj1095:Hide 捉迷藏(动态树分治)
- BZOJ 1095 ZJOI 2007 Hide 捉迷藏 动态点分治
- 【BZOJ1095】【ZJOI2007】捉迷藏 [动态点分治]
- bzoj1095 [ZJOI2007]Hide 捉迷藏(动态点分治+堆)
- [bzoj1095][ZJOI2007]Hide 捉迷藏(动态点分治)
- bzoj1095 [ZJOI2007]Hide 捉迷藏 括号序列/动态点分治/树的数据生成
- bzoj1095: [ZJOI2007]Hide 捉迷藏(动态点分治+树上ST表)
- [动态点分治] BZOJ1095: [ZJOI2007]Hide 捉迷藏
- bzoj 1095: [ZJOI2007]Hide 捉迷藏 (动态点分治)
- [BZOJ]1095 捉迷藏 动态点分治(点分树)
- bzoj1095 [ZJOI2007]Hide 捉迷藏(动态点分治|括号序列)
- 【BZOJ1095】捉迷藏(动态点分治)
- bzoj 1095 Hide 捉迷藏 - 动态点分治 -堆
- 【BZOJ1095】捉迷藏(动态点分治)
- 动态点分治:Bzoj1095: [ZJOI2007]Hide 捉迷藏
- BZOJ 1095 [ZJOI2007]Hide 捉迷藏 ——动态点分治
- 动态点分治:Bzoj1095: [ZJOI2007]Hide 捉迷藏
- BZOJ_1095_[ZJOI2007]Hide 捉迷藏_动态点分治+堆