【CF1205D】Almost All(构造)
- 给定一棵 \(n\) 个点的树,你需要给每条边赋一个非负权值。
- 设 \(S\) 为所有两点间距离构成的集合,要求 \(1\sim\lfloor\frac{2n^2}9\rfloor\) 都出现在 \(S\) 中。
- \(1\le n\le10^3\)
树的重心性质
先考虑菊花图,发现最优肯定是选 \(\frac n2\) 条边分别填 \(1,2,\cdots,\frac{n}2\),另 \(\frac n2\) 条边分别填 \(\frac n2,2\times\frac n2,\cdots,(\frac n2)^2\),这样就能得到出 \(1\sim \frac n2(\frac n2+1)\)。
又发现 \(\frac{2n^2}9=\frac n3\times\frac{2n}3\),而我们知道将重心的子树们贪心划成两部分可以让较小的那一部分点数大于等于 \(\frac n3\)。
那么只要让一部分所有点到重心的边权和分别为 \(1,2,\cdots,S1\),另一部分分别为 \(S1,2\times S1,\cdots,S1\times S2\) 即可。
按照 dfs 到的顺序从小到大定值,则边权都是非负的。
代码:\(O(n\log n)\)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Rg register
#define RI Rg int
#define Cn const
#define CI Cn int&
#define I inline
#define W while
#define N 1000
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
using namespace std;
int n,s[2],c[N+5],ee,lnk[N+5];struct edge {int to,nxt;}e[N<<1];pair<int,int> g[N+5];
int rt,Sz[N+5],Mx[N+5];void GetRt(CI x,CI lst=0)//找重心
{
Sz[x]=1,Mx[x]=0;for(RI i=lnk[x],y;i;i=e[i].nxt) (y=e[i].to)^lst&&(GetRt(y,x),Sz[x]+=Sz[y],Mx[x]=max(Mx[x],Sz[y]));
(Mx[x]=max(Mx[x],n-Sz[x]))<Mx[rt]&&(rt=x);
}
int t,w[N+5],d[N+5];void dfs(CI x,CI lst,CI pre,CI o)//定边权
{
d[x]=++t*o,w[pre]=d[x]-d[lst];
for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&(dfs(e[i].to,x,i+1>>1,o),0);
}
int main()
{
RI i,x,y;for(scanf("%d",&n),i=1;i^n;++i) scanf("%d%d",&x,&y),add(x,y),add(y,x);
RI ct=0;for(Mx[rt=0]=1e9,GetRt(1),GetRt(rt),i=lnk[rt];i;i=e[i].nxt) g[++ct]=make_pair(Sz[e[i].to],e[i].to);
for(sort(g+1,g+ct+1),i=ct;i;--i) s[0]<=s[1]?s[0]+=g[i].first:(s[1]+=g[i].first,c[g[i].second]=1);
RI S=s[0];for(i=lnk[rt];i;i=e[i].nxt) s[c[e[i].to]]-=Sz[e[i].to],t=s[c[e[i].to]],dfs(e[i].to,rt,i+1>>1,c[e[i].to]?S:1);//排序后贪心划分
for(i=1;i^n;++i) printf("%d %d %d\n",e[2*i-1].to,e[2*i].to,w[i]);return 0;
}
待到再迷茫时回头望,所有脚印会发出光芒