我是靠谱客的博主 坚定缘分,最近开发中收集的这篇文章主要介绍Union on Tree(毒瘤数据结构题)(点分树+虚树+树点覆盖去重)题解,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

There is a country whose road system is a tree, the nodes in the tree represent cities and the edge is the road between them, and each edge is of unit length. For safety, the government should put guards to protect all cities. But the government is in shortage of money. So they might not be able to protect all cites.

You're concerned about the country's safety, so you get the information of the guards. And you know that on the i-th day, there are k[i] guards on the road system, and j-th guard is on node a[j] and can protect every node whose distance to a[j] is not larger than r[j]. And you want to know how many nodes in the road system is protected by at least one guard.

Input

The first line contains an integer n, denoting the number of the cities.

Then n-1 lines follows, each line contains 2 integers a and b, denoting there is an edge between city a and b (1-based index).

The next line contains an integer Q, denoting the number of the days.

Then Q lines's description follows, each contains an integer k, denoting the number of guards in that day, and k integer pairs a[i],r[i] denoting the guards's information, for simplicity, no node will have more than 1 guard.

Constraints

  • 1 ≤ n, Q ≤ 50000.
  • The total number of guards in the queries ≤ 500000.

 

 

Output

For each day, output the answer in one line.

Example

Input:
20
1 2
1 3
1 4
4 5
4 6
2 7
4 8
5 9
7 10
2 11
9 12
8 13
1 14
12 15
9 16
7 17
12 18
1 19
6 20
5
2 9 3 12 5 
3 3 3 4 1 11 5 
3 3 3 10 4 19 2 
2 3 4 10 2 
5 5 4 11 2 16 2 18 1 19 2
Output:
16
16
13
16
18

 

题解

毒瘤题,写了我一晚上555555

查询一个点范围r内的点数可以建立点分树在线查询

然后对询问的点建立虚树

由于虚树上有些点有范围,有些点没有范围

我们需要统一一下,就把所有点的范围都设为它周围的点到它之后还能延伸出的最长长度

这样处理并不会影响答案,因为每个点所包含的点集一定会被某一个点(可能是它本身)的点集包含

这样处理有什么好处呢?

我们会发现能覆盖一个点的点集会在虚树上形成一个连通块

而树上的连通块一定也是一棵树,满足点-边=1

所以我们可以先把虚树上每个点的答案统计进来

一定会有一些点被重复算了

而每个点被计算的次数就是能覆盖它的点集的大小

所以我们只需要枚举虚树上的边,把两边端点重复覆盖的点数减去即可

这样就可以保证每个点都只被统计了一次

怎么求两边端点重复覆盖的点数呢?

我们可以找到虚树边上覆盖部分在原树上的中点,利用它覆盖的范围来求重复覆盖的点数

(如图,黑点为虚树点,红点为原树的点)

但是我们发现有些时候并不能找到中点

我们可以在建立原树的时候在两个点之间插入一个新点,表示这条边,在点分树内不统计它的答案即可

代码:(4KB警告)(考虑到此题没有修改操作,点分树可以不用套动态开点线段树,直接用一个vector记录前缀和)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
inline int gi()
{
	char c;int num=0,flg=1;
	while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
	while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
	return num*flg;
}
#define N 100005
#define LOG 16
const int INF=0x3f3f3f3f;
int n,m;
int fir[N],to[2*N],nxt[2*N],cnt;
void adde(int a,int b)
{
	to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;
	to[++cnt]=a;nxt[cnt]=fir[b];fir[b]=cnt;
	//printf("%d %dn",a,b);
}
int f[N][LOG+2],dep[N],dfn[N],dc;
void dfs(int u)
{
	dfn[u]=++dc;dep[u]=dep[f[u][0]]+1;
	for(int v,p=fir[u];p;p=nxt[p])
		if((v=to[p])!=f[u][0]){f[v][0]=u;dfs(v);}
}
namespace DFS{
	vector<int> T1[N],T2[N];
	int dfa[N],dep[N];
	int nrt,all,tmpsiz[N];bool vis[N];
	void findrt(int u,int ff){
		int mx=0;tmpsiz[u]=1;
		for(int v,p=fir[u];p;p=nxt[p]){
			if((v=to[p])!=ff&&!vis[v]){
				findrt(v,u);
				tmpsiz[u]+=tmpsiz[v];
				mx=max(mx,tmpsiz[v]);
			}
		}
		mx=max(mx,all-tmpsiz[u]);
		if(2*mx<=all)nrt=u;
	}
	int getrt(int u,int sz){
		all=sz;nrt=-INF;
		findrt(u,0);return nrt;
	}
	int mx1,mx2,tmp[N],dfn,dis[N][LOG+2];
	void pre(int u,int d,int ff){
		tmp[++dfn]=u;
		mx1=max(mx1,dis[u][d]);
		mx2=max(mx2,dis[u][d-1]);
		tmpsiz[u]=1;
		for(int v,p=fir[u];p;p=nxt[p]){
			if(!vis[v=to[p]]&&v!=ff){
				dis[v][d]=dis[u][d]+1;
				pre(v,d,u);
				tmpsiz[u]+=tmpsiz[v];
			}
		}
	}
	void DFZ(int u,int d){
		dep[u]=d;
		dfn=mx1=mx2=0;vis[u]=1;pre(u,d,0);
		for(int i=0;i<=mx1;i++)T1[u].push_back(0);
		for(int i=0;i<=mx2;i++)T2[u].push_back(0);
		for(int i=1;i<=dfn;i++){
			int x=tmp[i];
			if(x<=((n+1)>>1)){
				T1[u][dis[x][d]]++;
				T2[u][dis[x][d-1]]++;
			}
		}
		for(int i=1;i<=mx1;i++)T1[u][i]+=T1[u][i-1];
		for(int i=1;i<=mx2;i++)T2[u][i]+=T2[u][i-1];
		for(int v,p=fir[u];p;p=nxt[p])
			if(!vis[v=to[p]]){
				dfa[v=getrt(v,tmpsiz[v])]=u;
				DFZ(v,d+1);
			}
	}
	void work(){DFZ(getrt(1,n),1);}
	int query(int u,int k){
		if(k==-1)return 0;
		int ret=0;
		for(int t=u;t;t=dfa[t]){
			if(dfa[t]&&k-dis[u][dep[t]-1]>=0)
				ret-=T2[t][min((int)T2[t].size()-1,k-dis[u][dep[t]-1])];
			if(k-dis[u][dep[t]]>=0)
				ret+=T1[t][min((int)T1[t].size()-1,k-dis[u][dep[t]])];
		}
		return ret;
	}
}
int getk(int x,int k){for(int i=LOG;i>=0;i--)if(k&(1<<i))x=f[x][i];return x;}
int getd(int x,int d){return getk(x,dep[x]-d);}
int LCA(int x,int y)
{
	if(dep[x]<dep[y])swap(x,y);
	x=getd(x,dep[y]);
	if(x==y)return x;
	for(int i=LOG;i>=0;i--)
		if(f[x][i]!=f[y][i])
			x=f[x][i],y=f[y][i];
	return f[x][0];
}
bool cmp(int x,int y){return dfn[x]<dfn[y];}
int tmp[N],R[N],dcon,stk[N],top;bool ont[N];
void clean()
{
	for(int i=1;i<=dcon;i++)
		fir[tmp[i]]=0,ont[tmp[i]]=0,R[tmp[i]]=-1;
	cnt=0;
}
void build()
{
	//printf("build:n");
	sort(tmp+1,tmp+dcon+1,cmp);
	top=0;
	int tcon=dcon,lca,x;
	if(tmp[1]!=1)tmp[++dcon]=1,stk[++top]=1;
	for(int i=1;i<=tcon;i++){
		ont[x=tmp[i]]=1;
		if(top<=1||(lca=LCA(stk[top],x))==stk[top]){
			stk[++top]=x;
			continue;
		}
		while(top>1&&dep[stk[top-1]]>=dep[lca]){
			adde(stk[top-1],stk[top]);
			top--;
		}
		if(lca!=stk[top]){
			adde(lca,stk[top]);
			stk[top]=lca;tmp[++dcon]=lca;
		}
		stk[++top]=x;
	}
	while(top>1){
		adde(stk[top-1],stk[top]);
		top--;
	}
}
void dfs1(int u,int ff)
{
	for(int v,p=fir[u];p;p=nxt[p])
		if((v=to[p])!=ff){
			dfs1(v,u);
			R[u]=max(R[u],R[v]-(dep[v]-dep[u]));
		}
}
void dfs2(int u,int ff)
{
	for(int v,p=fir[u];p;p=nxt[p])
		if((v=to[p])!=ff){
			R[v]=max(R[u]-(dep[v]-dep[u]),R[v]);
			dfs2(v,u);
		}
}
int ans;
void solve(int u,int ff)
{
	ans+=DFS::query(u,R[u]);
	for(int v,p=fir[u];p;p=nxt[p]){
		if((v=to[p])!=ff){
			if(dep[u]+R[u]>=dep[v]-R[v]){
				int z=(dep[u]+R[u]+dep[v]-R[v])>>1,r=(dep[u]+R[u]-(dep[v]-R[v]))>>1;
				z=getd(v,z);
				ans-=DFS::query(z,r);
			}
			solve(v,u);
		}
	}
}
int main()
{
	int i,j,u,v;
	n=gi();
	for(i=1;i<n;i++){u=gi();v=gi();adde(u,n+i);adde(n+i,v);}
	n=2*n-1;
	dfs(1);
	for(j=1;j<=LOG;j++)for(i=1;i<=n;i++)f[i][j]=f[f[i][j-1]][j-1];
	DFS::work();
	memset(fir,0,sizeof(fir));cnt=0;
	memset(R,-1,sizeof(R));
	m=gi();
	while(m--){
		dcon=gi();
		for(i=1;i<=dcon;i++){tmp[i]=gi();R[tmp[i]]=gi()*2;}
		ans=0;build();dfs1(1,0);dfs2(1,0);solve(1,0);clean();
		printf("%dn",ans);
	}
}

 

 

 

 

 

 

 

最后

以上就是坚定缘分为你收集整理的Union on Tree(毒瘤数据结构题)(点分树+虚树+树点覆盖去重)题解的全部内容,希望文章能够帮你解决Union on Tree(毒瘤数据结构题)(点分树+虚树+树点覆盖去重)题解所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(53)

评论列表共有 0 条评论

立即
投稿
返回
顶部