CF1205D Almost All 解题报告

CF1205D Almost All 解题报告:

更好的阅读体验

题意

给定一颗 \(n\) 个点的树,构造树的边权使得对于 \(x\in[1,\lfloor\frac{2n^2}{9}\rfloor]\) 都存在一条路径使得路径边权和为 \(x\)

\(1\leqslant n\leqslant 1000\)

分析

nb 题。

首先容易构造出一种基本操作:对于一颗 \(n\) 个点的树,根到每个点的路径长度取遍 \(1,2,3,\cdots,n-1\),只需要跑出 dfn 序然后边权等于相邻点 dfn 序之差即可。

考虑选择一个根,将儿子分成两个部分 A 和 B(大小分别为 \(p,q\)),A 部分使用上面的方法做出 \(1,2,\cdots,p\) 的路径长度,B 部分做出 \(1,2,\cdots,q\) 的路径长度,然后将所有边权乘 \((p+1)\),容易发现这样可以拼凑出 \((p+1)(q+1)-1\) 的所有长度。

那么我们要选择出一组比较均衡的 \(p,q\) 满足 \((p+1)(q+1)-1\geqslant \lfloor\frac{2n^2}{9}\rfloor\)

考虑怎么让 \(p,q\) 均衡:

首先选择重心为根,那么所有子树大小小于等于 \(\lfloor\frac{n}{2}\rfloor\)

我们只需要将子树按照大小排序,选择一段前缀使得其恰好大于等于 \(\lfloor\frac{n}{3}\rfloor\) 即可,容易发现这些子树的大小之和不超过 \(2\lfloor\frac{n}{3}\rfloor\)

计算可得上述构造符合条件,于是直接做即可,时间复杂度 \(O(n\log n)\)

代码

#include<stdio.h>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn=1005;
int n,p,ids,dfns;
int sz[maxn],val[maxn],id[maxn],ok[maxn],fa[maxn],dfn[maxn];
vector<int>v[maxn];
void dfs(int x,int last,int f){
	sz[x]=1;
	for(int i=0;i<v[x].size();i++)
		if(v[x][i]!=last)
			dfs(v[x][i],x,f),sz[x]+=sz[v[x][i]],val[x]=max(val[x],sz[v[x][i]]);
	val[x]=max(val[x],n-sz[x]);
	if(f==0&&(p==0||val[p]>val[x]))
		p=x;
}
inline int cmp(int a,int b){
	return sz[a]<sz[b];
}
void get(int x,int last,int val){
	fa[x]=last,dfn[x]=(++dfns)*val;
	for(int i=0;i<v[x].size();i++)
		if(v[x][i]!=last)
			get(v[x][i],x,val);
}
int main(){
	scanf("%d",&n); 
	for(int i=1,x,y;i<n;i++)
		scanf("%d%d",&x,&y),v[x].push_back(y),v[y].push_back(x);
	dfs(1,0,0),dfs(p,0,1);
	for(int i=0;i<v[p].size();i++)
		id[++ids]=v[p][i];
	sort(id+1,id+1+ids,cmp);
	int sum=0,k;
	for(int i=1;i<=ids;i++){
		ok[id[i]]=1,sum+=sz[id[i]],get(id[i],p,1);
		if(sum>=n/3){
			k=i;
			break;
		}
	}
	dfns=0;
	for(int i=k+1;i<=ids;i++)
		get(id[i],p,sum+1);
	for(int i=1;i<=n;i++)
		if(i!=p)
			printf("%d %d %d\n",i,fa[i],dfn[i]-dfn[fa[i]]);
	return 0;
}
原文地址:https://www.cnblogs.com/xiaoziyao/p/15679743.html