[动态点分治] BZOJ1095: [ZJOI2007]Hide 捉迷藏
2017-02-15 11:00
330 查看
题意
给定N个节点的一棵树,一开始所有点都是黑色。需要执行Q个操作,操作有两种类型:1.改变单点的颜色(黑变白,白变黑)。
2.询问最远黑色点对的距离。
N ≤100000, M ≤500000
题解
动态点分治经典题,一般和树上路径有关的题目都需要往这方面考虑。对于每个点分树,我们把信息都收集到根节点上。
对于过某个根节点的路径,要求最长的一条:先对根节点的每个直接儿子求出以这个儿子为根的子树中的点到根节点的距离的最大值。在若干个最大值中挑最大的和次大的加和即是答案,我们的目标就是动态维护这些东西。
我们需要在子点分树上维护其中所有点到它父点分树根节点的距离,用一个堆h1[i]维护。
然后对每个父点分树的根节点,把它的子点分树的h1[i]的top全部收集起来放在堆里h2[i],还需要放一个0,表示根节点自己。
对于每个堆h2[i],把它的第一大和第二大的和加起来放在一个全局堆ans中。ans的堆顶即是总的答案。
注意我们只在堆中加入黑点,改变颜色操作对应的是添加/删除单点。
如何处理添加/删除呢?
因为深度是logn的,也就是说一个点最多存在于logn棵点分树中,所以直接暴力从下往上爬,途中更新一下他影响到的堆。
例如改变的点是x,当前x在的点分树的根为G, faG是父点分树的根:
1.删除h2[prt[G]]在ans中的影响。
2.删除h2[prt[G]]中对应x所在的子树的元素。
3.在h1[G]中添加/删除x。
4. 添加h2[prt[G]]中对应x所在的子树的元素。
5.添加h2[prt[G]]在ans中的影响。
#include<cstdio> #include<queue> #include<algorithm> using namespace std; const int maxn=100005, maxe=200005; int n,Q,sz[maxn],prt[maxn]; int fir[maxn],nxt[maxe],son[maxe],tot; bool vis[maxn],sta[maxn]; //-------------------------------------------------------------------------------------- void dfs_size(int x,int fa){ sz[x]=1; for(int j=fir[x];j;j=nxt[j]) if(son[j]!=fa&&!vis[son[j]]) dfs_size(son[j],x), sz[x]+=sz[son[j]]; } int allmin,root,allsz; void dfs_G(int x,int fa){ int _max=0; for(int j=fir[x];j;j=nxt[j]) if(son[j]!=fa&&!vis[son[j]]){ dfs_G(son[j],x); _max=max(_max,sz[son[j]]); } if(allmin>max(_max,allsz-sz[x])){ allmin=max(_max,allsz-sz[x]); root=x; } } int Find_G(int x){ allmin=1e+9; dfs_size(x,0); allsz=sz[x]; dfs_G(x,0); return root; } //-------------------------------------------------------------------------------------- struct Heap{ priority_queue< int > q, del; void push(int x){ q.push(x); } void erase(int x){ del.push(x); } void maintain(){ while(!del.empty()&&del.top()==q.top()) del.pop(), q.pop(); } int top(){ maintain(); return q.top(); } void pop(){ maintain(); q.pop(); } int sec_top(){ int tmp=top(); pop(); int res=top(); push(tmp); return res; } int size(){ return q.size()-del.size(); } } ans,h1[maxn],h2[maxn]; void Insert_ans(Heap &x){ if(x.size()>1) ans.push(x.top()+x.sec_top()); } void Erase_ans(Heap &x){ if(x.size()>1) ans.erase(x.top()+x.sec_top()); } //-------------------------------------------------------------------------------------- int dep[maxn],anc[maxn][25]; void dfs_LCA(int x,int fa){ anc[x][0]=fa; for(int j=1;j<=20;j++) anc[x][j]=anc[anc[x][j-1]][j-1]; for(int j=fir[x];j;j=nxt[j]) if(son[j]!=fa) dep[son[j]]=dep[x]+1, dfs_LCA(son[j],x); } int LCA(int x,int y){ if(dep[x]<dep[y]) swap(x,y); for(int j=20;j>=0;j--) if(dep[anc[x][j]]>=dep[y]) x=anc[x][j]; if(x==y) return x; for(int j=20;j>=0;j--) if(anc[x][j]!=anc[y][j]) x=anc[x][j], y=anc[y][j]; return anc[x][0]; } int getDis(int x,int y){ return dep[x]+dep[y]-dep[LCA(x,y)]*2; } //-------------------------------------------------------------------------------------- void work(int x,int fa,int id){ h1[id].push(getDis(x,prt[id])); for(int j=fir[x];j;j=nxt[j]) if(son[j]!=fa&&!vis[son[j]]) work(son[j],x,id); } int DivTree(int x,int faG){ int G=Find_G(x); prt[G]=faG; vis[G]=true; h2[G].push(0); work(G,0,G); for(int j=fir[G];j;j=nxt[j]) if(!vis[son[j]]){ int sonG=DivTree(son[j],G); if(h1[sonG].size()) h2[G].push(h1[sonG].top()); } Insert_ans(h2[G]); return G; } void Updata(int x,int k){ //k=0 delete k=1 insert Erase_ans(h2[x]); if(!k) h2[x].erase(0); else h2[x].push(0); Insert_ans(h2[x]); for(int y=x;prt[y];y=prt[y]){ Erase_ans(h2[prt[y]]); if(h1[y].size()) h2[prt[y]].erase(h1[y].top()); if(!k) h1[y].erase(getDis(x,prt[y])); else h1[y].push(getDis(x,prt[y])); if(h1[y].size()) h2[prt[y]].push(h1[y].top()); Insert_ans(h2[prt[y]]); } } //-------------------------------------------------------------------------------------- void add(int x,int y){ son[++tot]=y; nxt[tot]=fir[x]; fir[x]=tot; } int getint(){ int res=0; char ch=getchar(); while(!('0'<=ch&&ch<='9')) ch=getchar(); while('0'<=ch&&ch<='9') res=res*10+ch-'0', ch=getchar(); return res; } int main(){ freopen("bzoj1095.in","r",stdin); freopen("bzoj1095.out","w",stdout); scanf("%d",&n); for(int i=1;i<=n-1;i++){ int x=getint(),y=getint(); add(x,y); add(y,x); } dfs_LCA(1,0); DivTree(1,0); int cnt_now=n; scanf("%d",&Q); while(Q--){ char ch=getchar(); while(ch!='C'&&ch!='G') ch=getchar(); if(ch=='C'){ int x=getint(); Updata(x,sta[x]); cnt_now+=sta?1:-1; sta[x]^=1; } if(ch=='G'){ if(cnt_now<1) printf("%d\n",cnt_now-1); else printf("%d\n",ans.top()); } } return 0; }
相关文章推荐
- bzoj1095 [ZJOI2007]Hide 捉迷藏 括号序列/动态点分治/树的数据生成
- BZOJ 1095 ZJOI 2007 Hide 捉迷藏 动态点分治
- bzoj1095 [ZJOI2007]Hide 捉迷藏(动态点分治|括号序列)
- BZOJ_1095_[ZJOI2007]Hide 捉迷藏_动态点分治+堆
- bzoj1095: [ZJOI2007]Hide 捉迷藏(动态点分治+树上ST表)
- BZOJ 1095 [ZJOI2007]Hide 捉迷藏 ——动态点分治
- bzoj1095 [ZJOI2007]Hide 捉迷藏(动态点分治+堆)
- bzoj 1095: [ZJOI2007]Hide 捉迷藏 (动态点分治)
- BZOJ1095 [ZJOI2007]Hide 捉迷藏 【动态点分治 + 堆】
- [bzoj1095][ZJOI2007]Hide 捉迷藏(动态点分治)
- 动态点分治:Bzoj1095: [ZJOI2007]Hide 捉迷藏
- 动态点分治:Bzoj1095: [ZJOI2007]Hide 捉迷藏
- 【BZOJ1095】[ZJOI2007]Hide 捉迷藏【动态树分治】
- bzoj1095: [ZJOI2007]Hide 捉迷藏 线段树维护括号序列 点分治 链分治
- 【BZOJ1095】【ZJOI2007】捉迷藏 [动态点分治]
- bzoj1095[ZJOI2007]Hide 捉迷藏
- BZOJ 1095【ZJOI2007】Hide捉迷藏
- BZOJ 1095: [ZJOI2007]Hide 捉迷藏 动态树分治
- BZOJ1095: [ZJOI2007]Hide 捉迷藏
- 【BZOJ 1095】 [ZJOI2007]Hide 捉迷藏