zoukankan      html  css  js  c++  java
  • 【bzoj1095】 ZJOI2007—捉迷藏

    http://www.lydsy.com/JudgeOnline/problem.php?id=1095 (题目链接)

    题意

      一棵树,求最远的两黑点之间的距离,每次可以将黑点染白或者将白点染黑。

    Solution

      动态树分治,%%%重庆省选AK爷

      点分治的过程是对树块找重心之后分成多个小树块,降低规模分别处理的过程,把链的信息收到其中“最高重心”上,从所有的重心处像分治中的不同子树索取到重心的链,就可以覆盖所有链的信息。动态点分治就像把序列分治变成线段树一样,在分治的架子上加了信息维护,实现树链信息维护与查询。

      需要什么?

      每个重心需要其每个分离子树到它的信息(很重要,否则形成链的重复部分,并且还需要一个自己到自己的空信息维护单链上来的信息)

      每个重心需要它到父分治块的信息

      全局需要维护每个重心的信息

      因此,考虑问题的静态版本,需要维护每个节点每个子树内的最长链,那么为了修改,最大化应换成堆维护。

      每个重心维护一个堆,表示其分离子树中每个到它的最长链

      每个重心维护一个堆,表示其块内到父重心的最长链

      全局维护一个堆,表示每个重心处的最长链

      修改时只要从一个节点作为重心的块开始向上修改,就可以遍历到所有包含它的块,修改到父重心的信息,修改父重心的信息

      注意开始时的节点的分离子树那个堆要插一个0表示单链,而改掉节点值的时候0的存在性也要相应地变化。

    细节

      今天我机子真是出了鬼了,几个明显会WA的错误竟然拍不出来,搞得我都怀疑自己存在的价值了→_→(虽然我也不知道这二者之间有什么联系)

    代码

    // bzoj1095
    #include<algorithm>
    #include<iostream>
    #include<cstdlib>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<queue>
    #include<ctime>
    #define LL long long
    #define inf 1ll<<30
    #define Pi acos(-1.0)
    #define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
    using namespace std;
    
    const int maxn=100010;
    int light[maxn],bin[30],par[maxn],head[maxn];
    int n,Q;
    
    struct edge {int to,next;}e[maxn<<1];
    struct hope {
    	priority_queue<int> q,del;
    	int top() {
    		while (!del.empty() && q.top()==del.top()) q.pop(),del.pop();
    		return q.top();
    	}
    	void pop() {
    		while (!del.empty() && q.top()==del.top()) q.pop(),del.pop();
    		q.pop();
    	}
    	int size() {
    		return q.size()-del.size();
    	}
    	void push(int x) {
    		q.push(x);
    	}
    	void erase(int x) {
    		del.push(x);
    	}
    }q[maxn],d[maxn],ans;
    
    namespace LittleTrick {
    	int cnt,deep[maxn],fa[maxn][30];
    	
    	inline void link(int u,int v) {
    		e[++cnt]=(edge){v,head[u]};head[u]=cnt;
    		e[++cnt]=(edge){u,head[v]};head[v]=cnt;
    	}
    	inline void dfs(int x) {
    		for (int i=1;i<=20;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
    		for (int i=head[x];i;i=e[i].next) if (e[i].to!=fa[x][0]) {
    				deep[e[i].to]=deep[x]+1;
    				fa[e[i].to][0]=x;
    				dfs(e[i].to);
    			}
    	}
    	inline int lca(int x,int y) {
    		if (deep[x]<deep[y]) swap(x,y);
    		int t=deep[x]-deep[y];
    		for (int i=0;bin[i]<=t;i++) if (bin[i]&t) x=fa[x][i];
    		if (x==y) return x;
    		for (int i=20;i>=0;i--) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    		return fa[x][0];
    	}
    	inline void insert(hope &a) {
    		if (a.size()>1) {
    			int tmp=a.top();a.pop();
    			ans.push(tmp+a.top());a.push(tmp);
    		}
    	}
    	inline void erase(hope &a) {
    		if (a.size()>1) {
    			int tmp=a.top();a.pop();
    			ans.erase(tmp+a.top());a.push(tmp);
    		}
    	}
    	inline int dis(int x,int y) {
    		return deep[x]+deep[y]-2*deep[lca(x,y)];
    	}
    }
    using namespace LittleTrick;
    
    namespace NodeDivide {
    	int Dargen,sum;
    	int size[maxn],f[maxn],vis[maxn];
    	
    	inline void caldargen(int x,int fa) {
    		f[x]=0;size[x]=1;
    		for (int i=head[x];i;i=e[i].next) if (e[i].to!=fa && !vis[e[i].to]) {
    				caldargen(e[i].to,x);
    				size[x]+=size[e[i].to];
    				f[x]=max(f[x],size[e[i].to]);
    			}
    		f[x]=max(f[x],sum-size[x]);
    		if (f[x]<f[Dargen]) Dargen=x;
    	}
    	inline void caldeep(int x,int fa,int p) {
    		d[p].push(dis(x,par[p]));
    		for (int i=head[x];i;i=e[i].next)
    			if (e[i].to!=fa && !vis[e[i].to]) caldeep(e[i].to,x,p);
    	}
    	inline void work(int x) {
    		vis[x]=1;
    		q[x].push(0);
    		caldeep(x,0,x);
    		for (int i=head[x];i;i=e[i].next) if (!vis[e[i].to]) {
    				sum=size[e[i].to];Dargen=0;
    				caldargen(e[i].to,x);
    				par[Dargen]=x;// par[e[i].to]=x;
    				work(Dargen);//work(e[i].to);
    				//q[x].push(d[e[i].to].top());
    			}
    		q[par[x]].push(d[x].top());
    		insert(q[x]);
    	}
    	inline void Init() {
    		f[Dargen=0]=inf;sum=n;
    		caldargen(1,0);
    		work(Dargen);
    	}
    }
    using namespace NodeDivide;
    
    namespace Query {
    	inline void On(int x) {
    		erase(q[x]);
    		q[x].erase(0);
    		insert(q[x]);
    		for (int i=x;par[i];i=par[i]) {
    			erase(q[par[i]]);
    			if (d[i].size()) q[par[i]].erase(d[i].top());
    			d[i].erase(dis(par[i],x));
    			if (d[i].size()) q[par[i]].push(d[i].top());
    			insert(q[par[i]]);
    		}
    	}
    	inline void Off(int x) {
    		erase(q[x]);
    		q[x].push(0);
    		insert(q[x]);
    		for (int i=x;par[i];i=par[i]) {
    			erase(q[par[i]]);
    			if (d[i].size()) q[par[i]].erase(d[i].top());
    			d[i].push(dis(par[i],x));
    			if (d[i].size()) q[par[i]].push(d[i].top());
    			insert(q[par[i]]);
    		}
    	}
    }
    using namespace Query;
    
    int main() {
    	bin[0]=1;for (int i=1;i<=20;i++) bin[i]=bin[i-1]<<1;
    	scanf("%d",&n);
    	for (int u,v,i=1;i<n;i++) {
    		scanf("%d%d",&u,&v);
    		link(u,v);
    	}
    	dfs(1);
    	Init();
    	scanf("%d",&Q);
    	char ch[5];
    	for (int cnt=n,x,i=1;i<=Q;i++) {
    		scanf("%s",ch);
    		if (ch[0]=='G') {
    			if (cnt<=1) printf("%d
    ",cnt-1);
    			else printf("%d
    ",ans.top());
    		}
    		else {
    			scanf("%d",&x);
    			if (!light[x]) On(x),light[x]=!light[x],cnt--;
    			else Off(x),light[x]=!light[x],cnt++;
    		}
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    socket 第一课
    _getitem__ __setitem__ __delitem__ __len__
    单继承&多继承 注意点
    面对对象 类&对象
    异常检测
    模块导入
    序列化模块注意点 json&pickle
    re模块
    filter和map
    Maven入门
  • 原文地址:https://www.cnblogs.com/MashiroSky/p/6358130.html
Copyright © 2011-2022 走看看