Query on a tree IV(SPOJ - QTREE4)

题目描述:

You are given a tree (an acyclic undirected connected graph) with N nodes, and nodes numbered 1,2,3...,N. Each edge has an integer value assigned to it(note that the value can be negative). Each node has a color, white or black. We define dist(a, b) as the sum of the value of the edges on the path from node a to node b.

All the nodes are white initially.

We will ask you to perfrom some instructions of the following form:

  • C a : change the color of node a.(from black to white or from white to black)
  • A : ask for the maximum dist(a, b), both of node a and node b must be white(a can be equal to b). Obviously, as long as there is a white node, the result will alway be non negative.

给出一棵n个结点的树,每条边有边权。一开始,每个点都是白色的。
再给出q个询问,每次询问有两种操作:

1.C a 表示改变a结点的颜色,黑变白,白变黑。
2.A 表示询问树上某两个白点(可以是同一个点)间最远距离
注意原题卡常,可以尝试换用clang编译器提交。

思路:

QTree 5 的进化版。。(为甚是这个顺序我也不清楚。。)

主要操作和QTree4的操作差不多,详细内容可见那题题解:Query on a tree V

上一题的思想是在每个点开一个可删堆\(Q1\),然后利用点分树优化查询与更新,这次其实也差不多只不过它并没有给你确定其中一个点,那么接下来的操作就比较有趣了

为了避免多次枚举,我们在每个点上多开一个队列\(Q2\),存的内容是距离每个儿子上最远的点到自身的距离,听起来有些绕,好好理解一下。。然后每次更新时,只要注意当前的儿子的最远距离(记录在原先的那个队列中)——也就是\(Q1\)\(Top\)被更新掉时,就要对应的去更新它父亲的\(Q2\),注意要算上儿子到父亲的距离,为了方便,我的\(Q1\)中存的值就是算上它到父亲的距离的。那么若要找经过该点的最大距离时,就只用在它所对应的\(Q2\)中找出最大的两个值的和即可(注意如果只有一个值的时候,要返回0,因为题意说明可以是同一个点也算)

所以代码中最关键的两点就是对于这三种队列的更新了!仔细理解。

最后就是把每个点的最大值存在最后一个队列\(A\)中,同理每次一个点的\(Q2\)中的最大两值和改变时,都要在\(A\)中进行对应的更新(删除旧值,加入新值),那么最终查询答案只要返回\(A\)\(Top\)即可,至于没有白点的判断,只要维护一个\(tot\),计数白点数量,不必多说!

还有一个需要说的就是点分树的存法,由于这题时限,不能用常规\(log\)\(LCA\)来算距离,可以全部预处理出来存一个正向表(或\(vector\))中。存每个儿子的每个父亲以及距离,这些均可以在一个\(dfs\)中求出

代码

#include<cstdio>
#include<cstring>
#include<queue>
#define FOR(i,l,r) for(register int i=l,END=r;i<=END;i++)
#define DOR(i,r,l) for(register int i=r,END=l;i>=END;i--)
#define loop(i,n) for(register int i=0,END=n;i<END;i++)
#define mms(a,x) memset(a,x,sizeof a)
#define sf scanf
#define pf printf
using namespace std;
const int N=1e5+5,INF=1e9;
int n;
struct Graph{
	int tot,to[N*20],nxt[N*20],len[N*20],head[N];
	void add(int x,int y,int z){tot++;to[tot]=y;nxt[tot]=head[x];len[tot]=z;head[x]=tot;}
	void clear(){mms(head,-1);tot=0;}
	#define EOR(A,i,x) for(register int i=A.head[x];i!=-1;i=A.nxt[i])
}G,G2;//原树和点分树的路径 

struct Heap{//可删堆 
	priority_queue<int>Q,del;
	void Push(int x){if(x!=-INF)Q.push(x);}
	void Del(int x){if(x!=-INF)del.push(x);}
	void upd(){while(!del.empty()&&Q.top()==del.top())Q.pop(),del.pop();}
	int Size(){return Q.size()-del.size();}
	int Top(){upd();if(Q.empty())return -INF;return Q.top();}
	int Sum2(){
		upd();int siz=Size();
		if(!siz)return -INF;
		if(siz==1)return 0;
		int mx1=Top();Q.pop();
		int mx2=Top();Q.push(mx1);
		return max(mx1+mx2,0);
	}
}A,B[N],C[N];//C便是题解中的Q1,B便是题解中的Q2

bool col[N];//记录颜色 

struct DAC_Tree{//点分树 
	bool vis[N];
	int sz[N],mx[N],fa[N];
	int t_sz,center;
	void get_dis(int x,int f,int dis,int center){//预处理上面的G2 
		G2.add(x,center,dis);
		EOR(G,i,x){
			int v=G.to[i];
			if(v==f||vis[v])continue;
			get_dis(v,x,dis+G.len[i],center);
		}
	}
	void get_center(int x,int f){//寻找重心 
		sz[x]=1,mx[x]=0;
		EOR(G,i,x){
			int v=G.to[i];
			if(v==f||vis[v])continue;
			get_center(v,x);
			sz[x]+=sz[v];
			mx[x]=max(mx[x],sz[v]);
		}
		mx[x]=max(mx[x],t_sz-sz[x]);
		if(!center||mx[center]>mx[x])center=x;
	}
	void DAC(int x){//点分治 
		vis[x]=1;
		G2.add(x,x,0);
		EOR(G,i,x){
			int v=G.to[i];
			if(vis[v])continue;
			get_dis(v,x,G.len[i],x);
			center=0,t_sz=sz[v];
			get_center(v,x);
			fa[center]=x;
			DAC(center);
		}
	}
	
	//以下两个函数为本题关键,注意三个队列的更新 
	void insert(int x){//把一个点变白 
		int sum=B[x].Sum2();
		B[x].Push(0);
		int nsum=B[x].Sum2();
		if(nsum!=sum){
			A.Del(sum);
			A.Push(nsum);
		}
		EOR(G2,i,x){
			if(G2.nxt[i]==-1)break;
			int v=G2.to[i],f=G2.to[G2.nxt[i]],dis=G2.len[G2.nxt[i]],tp=C[v].Top();
			C[v].Push(dis);
			int ntp=C[v].Top();
			if(ntp!=tp){
				sum=B[f].Sum2();
				B[f].Del(tp);
				B[f].Push(dis);
				nsum=B[f].Sum2();
				if(nsum!=sum){
					A.Del(sum);
					A.Push(nsum);
				}
			}
		}
	}
	void erase(int x){//把一个点变黑 
		int sum=B[x].Sum2();
		B[x].Del(0);
		int nsum=B[x].Sum2();
		if(nsum!=sum){
			A.Del(sum);
			A.Push(nsum);
		}
		EOR(G2,i,x){
			if(G2.nxt[i]==-1)break;
			int v=G2.to[i],dis=G2.len[G2.nxt[i]],f=G2.to[G2.nxt[i]],tp=C[v].Top();
			C[v].Del(dis);
			int ntp=C[v].Top();
			if(ntp!=tp){
				sum=B[f].Sum2();
				if(tp!=-INF)B[f].Del(tp);
				B[f].Push(C[v].Top());
				nsum=B[f].Sum2();
				if(nsum!=sum){
					A.Del(sum);
					A.Push(nsum);
				}
			}			
		}
	}
	//预处理 
	void init(){
		center=0,t_sz=n;
		get_center(1,0);
		DAC(center);
		FOR(i,1,n)insert(i);
	}
}Tr;

int tot;
int main(){
	G.clear(),G2.clear();
	sf("%d",&n);
	loop(i,n-1){
		int x,y,z;
		sf("%d%d%d",&x,&y,&z);
		G.add(x,y,z),G.add(y,x,z);
	}
	Tr.init();
	int q;tot=n;
	sf("%d",&q);
	while(q--){
		char op[5];
		sf("%s",op);
		if(op[0]=='A'){
			if(!tot)pf("They have disappeared.\n");//无白点 
			else pf("%d\n",A.Top());
		}
		else { 
			int a;sf("%d",&a); 
			col[a]^=1;
			if(col[a]){Tr.erase(a);tot--;}
			else {Tr.insert(a);tot++;}
		}
	}
	return 0;
}
posted @ 2019-03-05 19:54  Hëinz  阅读(253)  评论(0)    收藏  举报