• [bzoj1905] [ZJOI2007] Hide 捉迷藏


    题意简述

    给定一棵 (n) 个点的树,起初每个点都为黑色。
    2种操作,要么改变某个点的颜色(由黑至白或由白至黑),要么询问距离最远的两个黑点间的距离。
    (m) 次操作。

    (nleq 10^5,mleq 2 imes 10^5)


    想法

    动态点分治模板题。

    如果只有一次询问操作,那么显然可以用点分治来做。(树形 (dp) 也可以,但那样不容易拓展到动态的情况)
    点分治时,以每个点为根时,统计过它的满足条件的路径即可。
    我们需要知道的只是以该点 (u) 的每个子节点 (v) 为根的子树到该点的最长距离 (mx[v])
    过该点的路径的最长距离为 所有 (mx[v]) 中的最大值+次大值。注意如果该点为黑点,最长距离则为 所有 (mx[v])(0) 中的最大值+次大值。
    同时我们应统计出以 (u) 为根的子树中所有点到上一个分治中心的距离的最大值 (mx[u])

    回到这道题,有修改和多次查询。
    于是建立 “点分树”,即将当前分治中心与它的各子节点的分治中心连边形成的树。
    这棵树有一些性质:
    1.只有 (logn)
    2.原树中以每个分治中心为根的子树里的所有点 就是 点分树中以它为根的子树中的所有点。
    3.修改一个点 (x) ,影响到的是点分树上所有 (x) 的祖先为分治中心的情况。
    设“点分树”中点 (u) 的父节点为 (pa[u])

    在此题中,对于每个点 (u),维护堆 (c[u]) 记录 (u) 为分治中心的子树中的所有黑点到 (pa[u]) 的距离,堆 (b[u]) 记录 (u) 为分治中心时各子节点到 (u) 的最大距离。即 (b[u]) 中的值,是所有 $c[v].top(),如果 (u) 为黑点则还需加上0。
    对全局维护堆 (a) 记录过每个分治中心的最长距离。(a) 中的值,就是 (b[u]) 中的最大值+次大值。

    由于有修改,所以堆需要满足可删除。
    用两个优先队列维护一个堆即可。

    还有一个问题,如何快速求原树上两点间距离?
    我们知道倍增和树剖都是 (O(logn)) 的,但更快的方法是 (st) 表+ (dfs) 序。
    这个 (dfs) 序很特殊,每次访问完 (u) 的子节点 (v) 后,要在序列中再加入 (u) 。记录进入每个点的时间 (dfn[u])
    在这个序列上用 (O(nlogn)) 预处理出 (st) 表,之后查询 (x)(y)(lca) 就是 (dfn[x])(dfn[y]) 着一段序列中深度最小的点,(O(1)) 可求。


    总结

    技巧

    1.(O(1)) 求静态树上两点的 (lca)(st) 表+ (dfs)

    int tot,dfn[N],num[N*2],dep[N];
    void dfs(int u){
    	int v;
    	dfn[u]=++tot; num[tot]=u;
    	for(node *p=h1[u];p;p=p->nxt)
    		if(!dfn[v=p->v]) {
    			dep[v]=dep[u]+1;
    			dfs(v);
    			num[++tot]=u; //与普通dfs序不同的地方!
    		}
    }
    int st[N*2][18],lg[N*2];
    void getst(){
    	dep[1]=1; dfs(1);
    	for(int i=1;i<=tot;i++) st[i][0]=dep[num[i]];
    	for(int j=1;j<18;j++){
    		int t=(1<<j);
    		for(int i=1;i+t-1<=tot;i++)
    			st[i][j]=min(st[i][j-1],st[i+t/2][j-1]);/**/
    	}
    	int t=0,cur=1;
    	for(int i=1;i<=tot;i++)
    		if(i<cur) lg[i]=t-1;
    		else {
    			lg[i]=t;
    			t++; cur*=2;
    		}
    }
    int lca(int x,int y){ //lca的深度
    	x=dfn[x]; y=dfn[y];
    	if(x>y) swap(x,y);
    	int t=lg[y-x+1];
    	return min(st[x][t],st[y+1-(1<<t)][t]);
    }
    

    2.可删堆
    用两个优先队列维护,一个维护所有的,一个维护删除的。

    struct heap{
    	priority_queue<int> q,d;
    	void ins(int x) { q.push(x); }
    	void del(int x) { d.push(x); }
    	void pop(){ //删除最大值
    		while(d.size() && d.top()==q.top()) q.pop(),d.pop();
    		q.pop();
    	}
    	int fr(){ //堆中最大值
    		while(d.size() && d.top()==q.top()) q.pop(),d.pop();
    		return q.top();
    	}
    	int size() { return q.size()-d.size(); }
    };
    

    手残

    (st) 表中注意第二维不要开小了!
    注意判断堆是否为空。


    代码

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<queue>
    
    using namespace std;
    
    int read(){
    	int x=0;
    	char ch=getchar();
    	while(!isdigit(ch)) ch=getchar();
    	while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    	return x;
    }
    
    const int N = 100005;
    
    int n;
    struct node{
    	int v;
    	node *nxt;
    }pool[N*4],*h1[N],*h[N];
    int cnt;
    void addedge1(int u,int v){
    	node *p=&pool[++cnt],*q=&pool[++cnt];
    	p->v=v;p->nxt=h1[u];h1[u]=p;
    	q->v=u;q->nxt=h1[v];h1[v]=q;
    }
    void addedge(int u,int v){
    	node *p=&pool[++cnt],*q=&pool[++cnt];
    	p->v=v;p->nxt=h[u];h[u]=p;
    	q->v=u;q->nxt=h[v];h[v]=q;
    }
    
    //get lca
    int tot,dfn[N],num[N*2],dep[N];
    void dfs(int u){
    	int v;
    	dfn[u]=++tot; num[tot]=u;
    	for(node *p=h1[u];p;p=p->nxt)
    		if(!dfn[v=p->v]) {
    			dep[v]=dep[u]+1;
    			dfs(v);
    			num[++tot]=u; /**/
    		}
    }
    int st[N*2][18],lg[N*2];
    void getst(){
    	dep[1]=1; dfs(1);
    	for(int i=1;i<=tot;i++) st[i][0]=dep[num[i]];
    	for(int j=1;j<18;j++){
    		int t=(1<<j);
    		for(int i=1;i+t-1<=tot;i++)
    			st[i][j]=min(st[i][j-1],st[i+t/2][j-1]);/**/
    	}
    	int t=0,cur=1;
    	for(int i=1;i<=tot;i++)
    		if(i<cur) lg[i]=t-1;
    		else {
    			lg[i]=t;
    			t++; cur*=2;
    		}
    }
    int lca(int x,int y){
    	x=dfn[x]; y=dfn[y];
    	if(x>y) swap(x,y);
    	int t=lg[y-x+1];
    	return min(st[x][t],st[y+1-(1<<t)][t]);
    }
    int dis(int x,int y) { 
    	return x==0||y==0?dep[x+y]:dep[x]+dep[y]-2*lca(x,y); 
    }
    
    //heap
    struct heap{
    	priority_queue<int> q,d;
    	void ins(int x) { q.push(x); }
    	void del(int x) { d.push(x); }
    	void pop(){
    		while(d.size() && d.top()==q.top()) q.pop(),d.pop();
    		q.pop();
    	}
    	int fr(){
    		while(d.size() && d.top()==q.top()) q.pop(),d.pop();
    		return q.top();
    	}
    	int se(){
    		int x=fr(); pop();
    		int y=fr(); q.push(x);
    		return x+y;
    	}
    	int size() { return q.size()-d.size(); }
    }a,b[N],c[N];
    
    //build dianfen tree
    int rt,root,all,sz[N],mx[N],vis[N],pa[N];
    void getrt(int u,int fa){
    	int v;
    	sz[u]=1; mx[u]=0;
    	for(node *p=h1[u];p;p=p->nxt)
    		if((v=p->v)!=fa && !vis[v]){
    			getrt(v,u);
    			sz[u]+=sz[v];
    			mx[u]=max(mx[u],sz[v]);
    		}
    	mx[u]=max(mx[u],all-sz[u]);
    	if(mx[u]<mx[rt]) rt=u;
    }
    void getsz(int u,int fa,int id){
    	int v;
    	sz[u]=1;
    	c[id].ins(dis(u,pa[id]));
    	for(node *p=h1[u];p;p=p->nxt)
    		if((v=p->v)!=fa && !vis[v]){
    			getsz(v,u,id);
    			sz[u]+=sz[v];
    		}
    }
    void work(int u){
    	int v;
    	vis[u]=1;
    	c[u].ins(dis(u,pa[u]));
    	b[u].ins(0);
    	for(node *p=h1[u];p;p=p->nxt)
    		if(!vis[v=p->v]){
    			getsz(v,u,u);
    			all=sz[v]; rt=0; getrt(v,u);
    			addedge(u,rt);
    			pa[rt]=u; v=rt;
    			work(rt);
    			b[u].ins(c[v].fr());
    		}
    	if(b[u].size()>1) a.ins(b[u].se());
    }
    
    //modify
    void turn_off(int u){//1->0
    	int x=u;
    	if(b[x].size()>1) a.del(b[x].se());
    	b[x].ins(0);
    	if(b[x].size()>1) a.ins(b[x].se());
    	while(x){
    		if(pa[x]) { //del fa
    			if(b[pa[x]].size()>1) a.del(b[pa[x]].se());
    			if(c[x].size()) b[pa[x]].del(c[x].fr()); /**/
    		}
    		c[x].ins(dis(u,pa[x]));
    		if(pa[x]) { //update fa
    			b[pa[x]].ins(c[x].fr());
    			if(b[pa[x]].size()>1) a.ins(b[pa[x]].se());
    		}
    		x=pa[x]; /**/
    	}
    }
    void turn_on(int u){//0->1
    	int x=u;
    	if(b[x].size()>1) a.del(b[x].se());
    	b[x].del(0);
    	if(b[x].size()>1) a.ins(b[x].se());
    	while(x){
    		if(pa[x]) { //del fa
    			if(b[pa[x]].size()>1) a.del(b[pa[x]].se());
    			if(c[x].size()) b[pa[x]].del(c[x].fr()); /**/
    		}
    		c[x].del(dis(u,pa[x]));
    		if(pa[x]) { //update fa
    			if(c[x].size()) b[pa[x]].ins(c[x].fr());/**/
    			if(b[pa[x]].size()>1) a.ins(b[pa[x]].se());
    		}
    		x=pa[x];/**/
    	}
    }
    
    int lon,lit[N];
    
    int main()
    {
    	int Q,x;
    	char ch[2];
    	n=read();
    	for(int i=1;i<n;i++) addedge1(read(),read());
    	
    	getst();
    	rt=0; mx[rt]=n+1; all=n; getrt(1,0);
    	root=rt;
    	work(root);
    	
    	lon=0;
    	Q=read();
    	while(Q--){
    		scanf("%s",ch);
    		if(ch[0]=='C'){
    			x=read();
    			if(lit[x]) lon--,turn_off(x),lit[x]=0; /*lit*/
    			else lon++,turn_on(x),lit[x]=1;
    		}
    		else{
    			if(lon==n) printf("-1
    ");
    			else if(lon==n-1) printf("0
    ");
    			else printf("%d
    ",a.fr());
    		}
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    【转】做好测试计划和测试用例工作的关键
    【转】RESTful Web Services初探
    最快排序和搜索算法的最简代码实现_转
    排序算法
    libevent简述
    linux异步IO--aio
    长志气戒傲气 必须时刻保持冷静
    LACP-链路聚合
    AM335x移植linux内核_转
    4种用于构建嵌入式linux系统的工具_转
  • 原文地址:https://www.cnblogs.com/lindalee/p/12373538.html
Copyright © 2020-2023  润新知