CSP-S 2019 Day 2 T3 树的重心

CSP-S 2019 Day 2 T3 树的重心

给出了一个大小为\(n\)的树,树中结点从 1∼n 编号。小简单的课后作业是求出这棵树单独删去每条边后,分裂出的两个子树的重心编号和之和。

\(n\le 10^5\)

思路

计算每一个点对答案单独的贡献。

删除一条边等价于把一个子树分裂出来,这个子树的根节点设为\(v\)

对于一个点\(x\),大致有以下几种情况。

  • 它在被分裂出来的子树内(此时\(v\)\(x\)的祖先或就是\(x\)
  • 被分裂出来的子树在\(x\)
  • 被分裂出来的子树在\(x\)的其他子树内
  • 被分裂出来的子树在\(x\)的重子树内

我们一一来处理一下

对于第一种情况,有可能过重的子树只有两个:\(x\)外的子树和\(x\)的重子树

对于第二种情况,有可能过重的子树只有两个:\(x\)外的子树和\(x\)的重子树

对于第三种情况,有可能过重的子树只有两个:\(x\)外的子树和\(x\)的重子树

对于第四种情况,,有可能过重的子树只有三个:\(x\)外的子树,\(x\)的重子树\(x\)的次重子树

列出对应的不等式,可以发现\(siz_v\)的取值有一个范围

另外\(v\)处在哪里还会有一些限制

把一个范围拆成两个,然后离线下来,扫描线。

代码

#include<bits/stdc++.h>
#define go(x) for(int i=head[x];i;i=edge[i].nxt)
#define now edge[i].v
using namespace std;
typedef long long ll;
const int sz=3e5+7;
int n,T;
ll Ans;
int t,m,tot;
int u,v,cnt;
int ans[sz];
int head[sz];
int a[sz];
int f[sz];
int st[sz],ed[sz];//子树内dfn的起点和终点 
int siz[sz],mx[sz],cl[sz],son[sz];//子树大小,最大子树,次大子树,重儿子 
struct Que{
	int id,tp,rl;
	const bool operator <(Que const & p)const{
		return rl<p.rl;
	}
}q[sz<<1];
struct Edge{
	int v,nxt;
}edge[sz<<1];
void make_edge(int u,int v){
	edge[++cnt]=(Edge){v,head[u]};head[u]=cnt;
	edge[++cnt]=(Edge){u,head[v]};head[v]=cnt;
}
bool cmp(int x,int y){
	return siz[x]<siz[y];
}
void add(int x,int sum){
	for(;x<=n;x+=x&-x) f[x]+=sum;
}
int query(int x){
	int ret=0;
	for(;x;x-=x&-x) ret+=f[x];
	return ret;
}
int Query(int l,int r){
	int ret=0;
	if(l>n||r<1) return 0;
	int R=min(r,n),L=max(l,1);
	ret=query(R)-query(L-1);
	return ret;
}
void dfs(int x,int fa){
	st[x]=++t;
	siz[x]=1;
	mx[x]=cl[x]=son[x]=0;
	go(x) if(now!=fa){
		dfs(now,x);
		siz[x]+=siz[now];
		if(siz[now]>mx[x]) cl[x]=mx[x],mx[x]=siz[now],son[x]=now;
		else if(siz[now]>cl[x]) cl[x]=siz[now];
	}
	ed[x]=t;
}
void Dfs(int x,int fa){
	ans[x]-=Query(n-2*siz[x],n-2*mx[x]);
	if(x^1) add(siz[x],1);
	ans[x]+=Query(2*mx[x],2*siz[x]);
	go(x) if(now!=fa) Dfs(now,x);
	if(x^1) add(siz[x],-1);
}
void work(){
	scanf("%d",&n);
	cnt=t=0;
	for(int i=1;i<=n;i++) head[i]=ans[i]=f[i]=0;
	for(int i=1;i<n;i++){
		scanf("%d%d",&u,&v);
		make_edge(u,v);
	}
	dfs(1,0);
	Dfs(1,0);
	for(int i=1;i<n;i++) a[i]=i+1;
	sort(a+1,a+n,cmp);
	
	m=0;
	for(int i=1;i<=n;i++){
		q[++m]=(Que){i,-1,n-2*siz[i]-1};
		q[++m]=(Que){i,1,n-2*mx[i]};
	}
	sort(q+1,q+m+1);
	tot=1;
	for(int i=1;i<=n;i++) f[i]=0;
	for(int i=1;i<=m;i++){
		while(tot<n&&siz[a[tot]]<=q[i].rl){
			add(st[a[tot]],1);
			tot++;
		}
		int x=q[i].id;
		ans[x]+=(Query(1,n)-Query(st[x],ed[x]))*q[i].tp;
	}
	
	m=0;
	for(int i=1;i<=n;i++)
		q[++m]=(Que){i,1,min(2*siz[i]-n,n-2*mx[i])};
	sort(q+1,q+m+1);
	tot=1;
	for(int i=1;i<=n;i++) f[i]=0;
	for(int i=1;i<=m;i++){
		while(tot<n&&siz[a[tot]]<=q[i].rl){
			add(st[a[tot]],1);
			tot++;
		}
		int x=q[i].id;
		ans[x]+=(Query(st[x],ed[x])-Query(st[son[x]],ed[son[x]])-Query(st[x],st[x]))*q[i].tp;
	}
	
	m=0;
	for(int i=1;i<=n;i++){
		if(2*mx[i]-n>min(2*siz[i]-n,n-2*cl[i])) continue;
		q[++m]=(Que){i,-1,2*mx[i]-n-1};
		q[++m]=(Que){i,1,min(2*siz[i]-n,n-2*cl[i])};
	}
	sort(q+1,q+m+1);
	tot=1;
	for(int i=1;i<=n;i++) f[i]=0;
	for(int i=1;i<=m;i++){
		while(tot<n&&siz[a[tot]]<=q[i].rl){
			add(st[a[tot]],1);
			tot++;
		}
		int x=q[i].id;
		ans[x]+=Query(st[son[x]],ed[son[x]])*q[i].tp;
	}
	Ans=0;
	for(int i=1;i<=n;i++) Ans+=1ll*i*ans[i];
	printf("%lld\n",Ans);
}
int main(){
	scanf("%d",&T);
	while(T--) work();
}
posted @ 2019-12-03 15:42  霞光  阅读(355)  评论(0编辑  收藏  举报