zoukankan      html  css  js  c++  java
  • BZOJ_1095_[ZJOI2007]Hide 捉迷藏_动态点分治+堆

    BZOJ_1095_[ZJOI2007]Hide 捉迷藏_动态点分治+堆

    Description

      捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子。某天,Jiajia、Wind和孩子们决定在家里玩
    捉迷藏游戏。他们的家很大且构造很奇特,由N个屋子和N-1条双向走廊组成,这N-1条走廊的分布使得任意两个屋
    子都互相可达。游戏是这样进行的,孩子们负责躲藏,Jiajia负责找,而Wind负责操纵这N个屋子的灯。在起初的
    时候,所有的灯都没有被打开。每一次,孩子们只会躲藏在没有开灯的房间中,但是为了增加刺激性,孩子们会要
    求打开某个房间的电灯或者关闭某个房间的电灯。为了评估某一次游戏的复杂性,Jiajia希望知道可能的最远的两
    个孩子的距离(即最远的两个关灯房间的距离)。 我们将以如下形式定义每一种操作: C(hange) i 改变第i个房
    间的照明状态,若原来打开,则关闭;若原来关闭,则打开。 G(ame) 开始一次游戏,查询最远的两个关灯房间的
    距离。

    Input

      第一行包含一个整数N,表示房间的个数,房间将被编号为1,2,3…N的整数。接下来N-1行每行两个整数a, b,
    表示房间a与房间b之间有一条走廊相连。接下来一行包含一个整数Q,表示操作次数。接着Q行,每行一个操作,如
    上文所示。

    Output

      对于每一个操作Game,输出一个非负整数到hide.out,表示最远的两个关灯房间的距离。若只有一个房间是关
    着灯的,输出0;若所有房间的灯都开着,输出-1。

    Sample Input

    8
    1 2
    2 3
    3 4
    3 5
    3 6
    6 7
    6 8
    7
    G
    C 1
    G
    C 2
    G
    C 1
    G

    Sample Output

    4
    3
    3
    4

    HINT

    对于100%的数据, N ≤100000, M ≤500000。


    终于看明白点分树是啥了。。。

    点分树上每个节点相当于维护他管辖的那些点。然后根据树高log可以支持一些操作。

    不过由于点分树上的边不一定在原树上存在,通常我们需要求两点间距离,这时用O(nlogn)-O(1)的ST表就比较快。

    说回这道题,开3个堆。h1[x]表示点分树上x的父亲到所管辖的所有点的距离。

    这步空间为nlogn,因为每个点最多出现log次。

    h2[x]存点分树上所有儿子的h1[son]堆顶。

    h3存全局所有h2的最大值和最小值。

    每次修改需要堆修改,再开一个堆即可。

    代码:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <queue>
    #include <ext/pb_ds/priority_queue.hpp>
    using namespace std;
    using namespace __gnu_pbds;
    #define N 100050
    #define GG puts("FUCK")
    int n,head[N],to[N<<1],nxt[N<<1],cnt,totot;
    int root,f[20][N<<1],g[N],used[N],sum,siz[N],tot,sta[N],pos[N],Lg[N<<1],dep[N],fa[N];
    inline char nc() {
    	static char buf[100000],*p1,*p2;
    	return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
    }
    int rd() {
    	int x=0; char s=nc();
    	while(s<'0'||s>'9') s=nc();
    	while(s>='0'&&s<='9') x=(x<<3)+(x<<1)+s-'0',s=nc();
    	return x;
    }
    char rc() {
    	char s=nc();
    	while(s!='G'&&s!='C') s=nc();
    	return s;
    }
    struct Heap {
    	std::priority_queue<int>q,p;
    	void update() {
    		while((!q.empty())&&(!p.empty())&&q.top()==p.top()) q.pop(),p.pop();
    	}
    	void push(int x) {q.push(x);update();}
    	void pop() {q.pop();update();}
    	void del(int x) {p.push(x);update();}
    	int top() {update(); return q.top();}
    	int siz() {return q.size()-p.size();}
    	int num() {
    		int a=top(); pop();
    		int b=top(); push(a);
    		return a+b;
    	}
    }h1[N],h2[N],h3;
    void lca_init() {
    	int i,j;
    	Lg[0]=-1;
    	for(i=1;i<=tot;i++) Lg[i]=Lg[i>>1]+1;
    	for(i=1;(1<<i)<=tot;i++) {
    		for(j=1;j+(1<<i)-1<=tot;j++) {
    			f[i][j]=min(f[i-1][j],f[i-1][j+(1<<(i-1))]);
    		}
    	}
    }
    inline void add(int u,int v) {
    	to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt;
    }
    void dfs(int x,int y) {
    	int i;
    	f[0][++tot]=dep[x]; pos[x]=tot;
    	for(i=head[x];i;i=nxt[i]) {
    		if(to[i]!=y) {
    			dep[to[i]]=dep[x]+1;
    			dfs(to[i],x);
    			f[0][++tot]=dep[x];
    		}
    	}
    }
    int dis(int x,int y) {
    	int dx=dep[x],dy=dep[y];
    	x=pos[x],y=pos[y]; if(x>y) swap(x,y);
    	int len=Lg[y-x+1];
    	int lcadep=min(f[len][x],f[len][y-(1<<len)+1]);
    	return dx+dy-2*lcadep;
    }
    void get_root(int x,int y) {
    	int i; siz[x]=1; g[x]=0;
    	for(i=head[x];i;i=nxt[i]) {
    		if(!used[to[i]]&&to[i]!=y) {
    			get_root(to[i],x);
    			siz[x]+=siz[to[i]];
    			g[x]=max(g[x],siz[to[i]]);
    		}
    	}
    	g[x]=max(g[x],sum-siz[x]);
    	if(g[x]<g[root]) root=x;
    }
    void solve(int x) {
    	int i; used[x]=1;
    	for(i=head[x];i;i=nxt[i]) {
    		if(!used[to[i]]) {
    			sum=siz[to[i]]; root=0;
    			get_root(to[i],0);
    			fa[root]=x;
    			solve(root);
    		}
    	}
    }
    void erase(int p) {if(h2[p].siz()>=2) h3.del(h2[p].num());}
    void insert(int p) {if(h2[p].siz()>=2) h3.push(h2[p].num());}
    void guanshang(int x) {
    	erase(x); h2[x].push(0); insert(x);
    	int t;
    	for(t=x;fa[t];t=fa[t]) {
    		erase(fa[t]);
    		if(h1[t].siz()) h2[fa[t]].del(h1[t].top());
    		h1[t].push(dis(fa[t],x)); h2[fa[t]].push(h1[t].top());
    		insert(fa[t]);
    	}
    }
    void dakai(int x) {
    	erase(x); h2[x].del(0); insert(x);
    	int t;
    	for(t=x;fa[t];t=fa[t]) {
    		erase(fa[t]);
    		h2[fa[t]].del(h1[t].top()); h1[t].del(dis(fa[t],x));
    		// printf("%d
    ",dis(fa[t],x));
    		if(h1[t].siz()) h2[fa[t]].push(h1[t].top());
    		insert(fa[t]);
    	}
    }
    int main() {
    	n=rd();
    	int i,x,y;
    	for(i=1;i<n;i++) {
    		x=rd(); y=rd(); add(x,y); add(y,x);
    	}
    	dfs(1,0); lca_init();
    	g[0]=1<<30; sum=n; get_root(1,0); solve(root);
    	// for(i=1;i<=n;i++) printf("%d
    ",fa[i]);
    	for(i=1;i<=n;i++) guanshang(i),sta[i]=1;
    	int m=rd(); totot=n;
    	while(m--) {
    		char s=rc();
    		if(s=='G') {
    			if(totot<=1) printf("%d
    ",totot-1);
    			else printf("%d
    ",h3.top());
    		}else {
    			x=rd();
    			// printf("%d
    ",x);
    			if(sta[x]) {
    				totot--; sta[x]=0;
    				dakai(x);
    			}else {
    				totot++; sta[x]=1;
    				guanshang(x);
    			}
    		}
    	}
    }
    
  • 相关阅读:
    python学习笔记(十五)-- flask接口开发
    python学习笔记(十四)-- requests接口调用
    python学习笔记(十三)-- 日志、发送邮件、redis数据库
    python学习笔记(十二)-- if __name__ == '__main__'
    python学习笔记(十一)-- md5加密
    python之操作excel:xlrd、xlwt、xlutiles、枚举函数enumerate()
    python:加密模块
    模块操作
    python基础:函数传参、全局变量、局部变量、内置函数、匿名函数、递归、os模块、time模块、解包
    【2019.6.2】python:json操作、函数、集合、random()、列表生成式、三元表达式
  • 原文地址:https://www.cnblogs.com/suika/p/9238069.html
Copyright © 2011-2022 走看看