[COCI 2016-2017#6] Sirni
阿新 • • 發佈:2021-08-10
\(\text{Problem}:\)Sirni
\(\text{Solution}:\)
當 \(P_{a}>P_{b}\) 時,連線 \(a,b\) 的代價即為 \(P_{a}\text{ mod }P_{b}\)。這樣去掉了 \(\min\)。
首先將權值相同的點合併。對於權值為 \(x\) 的點,我們只需對於每個 \(k\),找到滿足 \(kx\leq w<(k+1)x\) 的最小的 \(w\)(當 \(k=1\) 時為 \(x<w<2x\)),新增一條連線 \(x\) 與 \(w\) 的邊即可。簡單證明:假設存在 \(w_{2}\),滿足 \(kx\leq w<w_{2}<(k+1)x\)
總邊數為 \(O(V\log V)\),而邊權最大值小於 \(V\),故利用用桶排序然後求出最小生成樹即可。總時間複雜度 \(O(n+V\log V\cdot \alpha(V))\),可以通過。
\(\text{Code}:\)
夜畔流離回,暗歎永無殿。 獨隱萬花翠,空寂亦難遷。 千秋孰能為,明滅常久見。 但得心未碎,踏遍九重天。#include <bits/stdc++.h> //#pragma GCC optimize(3) //#define int long long #define ri register #define mk make_pair #define fi first #define se second #define pb push_back #define eb emplace_back #define is insert #define es erase #define vi vector<int> #define vpi vector<pair<int,int>> using namespace std; const int N=10000010; inline int read() { int s=0, w=1; ri char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); } while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar(); return s*w; } int n,a[N],up,f[N],lar[N]; long long ans; vpi g[N]; inline int Find(int x) { return f[x]^x?f[x]=Find(f[x]):x; } signed main() { n=read(); for(ri int i=1;i<=n;i++) { int x=read(); a[x]++, up=max(up,x); lar[x]=x; } for(ri int i=up-1;i;i--) { if(!lar[i]) lar[i]=lar[i+1]; } for(ri int i=1;i<=up;i++) { if(!a[i]) continue; for(ri int j=i;j<=up;j+=i) { int l=(j==i)?(i+1):(j), r=min(j+i-1,up); if(lar[l]<=r) g[lar[l]%i].eb(mk(i,lar[l])); } } for(ri int i=1;i<=up;i++) f[i]=i; for(ri int i=0;i<=up;i++) { for(auto j:g[i]) { int x=Find(j.fi), y=Find(j.se); if(x==y) continue; ans+=i, f[x]=y; } } printf("%lld\n",ans); return 0; }