1. 程式人生 > 其它 >【CF1205D】Almost All(構造)

【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)\)

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