[DarkBZOJ3754] Tree之最小方差樹
阿新 • • 發佈:2021-10-02
暴力真的能出奇跡!
。
前言
感覺如果不講做法應該要想上好一會,甚至可能做不出來。
題目
講解
拿到這道題很有可能沒什麼思路,我們就從暴力入手。
先把標準差的公式寫出來:
\[\sqrt{\frac{\sum_{0\le i<n}(a_i-\overline{a})^2}{n}} \]對於這道題,顯然我們知道生成樹是 \(n-1\) 條邊,而且外面的根號其實不影響相對大小,所以我們去掉這些東西( \(a_i\) 為邊權):
\[\sum (a_i-\overline{a})^2 \]顯然我們如果知道了 \(\overline{a}\) 的大小,就可以通過最小生成樹演算法求出上面這個東西的最小值,可惜我們不知道。
但我們真的不能知道嗎?
寫出它的表示式:\(\overline{a}=\frac{\sum a_i}{n-1}\) ,顯然 \(\overline{a}\) 由 \(\sum a_i\) 決定,我們發現 \(\sum a_i\) 最多不過 \(10^4\),直接列舉即可。
但是這還不夠,如果你列舉一次做一次 \(\tt Kruskal\),發現複雜度達到了 \(O(cnm\log_2m)\),是有問題的。
其實我們只需要一次排序,列舉一次的時候用雙指標維護一下當前情況下權值 \((a_i-\overline{a})\) 最小,且原始權值 \(a_i\) 小於和大於平均值的邊,並不難實現。
於是複雜度就是 \(O(m\log_2m+cnm)\)
我真不想說髒話。(攤手
程式碼
不懂戳我
//12252024832524 #include <cmath> #include <cstdio> #include <cstring> #include <algorithm> #define TT template<typename T> using namespace std; typedef long long LL; const int MAXN = 105; const int MAXM = 2005; const int INF = 0x3f3f3f3f; int n,m; double ans = INF; LL Read() { LL x = 0,f = 1; char c = getchar(); while(c > '9' || c < '0'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();} return x * f; } TT void Put1(T x) { if(x > 9) Put1(x/10); putchar(x%10^48); } TT void Put(T x,char c = -1) { if(x < 0) x = -x,putchar('-'); Put1(x); if(c >= 0) putchar(c); } TT T Max(T x,T y){return x > y ? x : y;} TT T Min(T x,T y){return x < y ? x : y;} TT T Abs(T x){return x < 0 ? -x : x;} struct edge { int u,v,w; bool operator < (const edge &px)const{ return w < px.w; } }e[MAXM]; int f[MAXN]; int findSet(int x){if(x^f[x])f[x]=findSet(f[x]);return f[x];} void unionSet(int u,int v){f[findSet(u)] = findSet(v);} int main() { // freopen(".in","r",stdin); // freopen(".out","w",stdout); n = Read(); m = Read(); for(int i = 1;i <= m;++ i) e[i].u = Read(),e[i].v = Read(),e[i].w = Read(); sort(e+1,e+m+1); e[0].w = -INF; e[m+1].w = INF; for(int C = 1;C <= 100*(n-1);++ C) { for(int i = 1;i <= n;++ i) f[i] = i; int cnt = n-1,s = 0; double av = C / (n-1.0),cur = 0; int r = upper_bound(e+1,e+m+2,edge{0,0,(int)ceil(av)}) - e; int l = r-1; while(cnt) { while(l > 0 && findSet(e[l].u) == findSet(e[l].v)) --l; while(r <= m && findSet(e[r].u) == findSet(e[r].v)) ++r; double L = av-e[l].w,R = e[r].w-av; if(L < R) cur += L*L,s += e[l].w,unionSet(e[l].u,e[l].v),--l; else cur += R*R,s += e[r].w,unionSet(e[r].u,e[r].v),++r; --cnt; } if(s == C) ans = Min(ans,sqrt(cur/(n-1))); } printf("%.4f",ans); return 0; }