[COCI 2016-2017#6] Sirni
\(\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\) 且权值区间 \((w,w2)\) 中没有结点(否则是一个子问题),那么根据上面的连边方式,一定存在 \(x\rightarrow w\) 与 \(w\rightarrow w_{2}\) 这两条边,而这两边的权值和与 \(x\rightarrow w_{2}\) 这条边的权值相同。故连 \(x\rightarrow w_{2}\) 一定不会更优。
总边数为 \(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;
}
夜畔流离回,暗叹永无殿。
独隐万花翠,空寂亦难迁。
千秋孰能为,明灭常久见。
但得心未碎,踏遍九重天。