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;
}