jzoj【NOIP2017提高A组冲刺11.2】失格

题目

Time Limits: 7000 ms Memory Limits: 786432 KB
Description

胆小鬼连幸福都会害怕,碰到棉花都会受伤,有时还被幸福所伤。
——太宰治《人间失格》

回顾我的一生,一共有n个事件,每一个事件有一个幸福值p_i。
我想用n-1条线把所有的事件连起来,变成一个连通块。一条连接了事件x和事件y的线会产生min(p_x mod p_y,p_y mod p_x)的喜悦值。
日日重复同样的事,遵循着与昨日相同的惯例,若能避开猛烈的狂喜,自然也不会有悲痛的来袭。因此,我想知道连接起来之后产生喜悦值最小是多少。

Input

文件第一行有一个正整数n。
接下来n行,每行一个正整数p_i。

Output

输出只有一行,表示最小的喜悦值。

Sample Input

输入1:
4
2
6
3
11
输入2:
4
1
2
3
4
输出3:
3
4
9
15

Sample Output

输出1:
1
输出2:
0
输出3:
4

Data Constraint

对于30%的数据,保证1<=n<=10^3。
对于另外40%的数据,保证1<=p_i<=10^6。
对于100%的数据,保证1<=n<=10^5,1<=p_i<=10^7。

题解

设序列中一个点值为pi,我们可以把所有大于pi*x的p中的最小的点和i连边,这样连出来的图一定包含了这个图的最小生成树
为什么呢?
不妨设(pi,pj)不满足上述条件,那么就一定有 pix<pc1<pc2...<pck<pj<pi(x+1) 那么我们发现连(i,c1),(c1,c2)…(ck,j)这些边的值得和和直接连的值时一样的

可是我们要求的是最小生成树啊,要选确定量的边
既然效果相同,为什么不选这种能多连几条边的方法呢?
如果形成了环不就可以删去一些边使得答案更小了吗?
如此就证明了这样选边一定不会更劣只会更优

注意排序要用计数排序,快排会t

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fo1(i,b,a) for(i=b;i>=a;i--)
using namespace std;

const int maxn=1e5+5,maxp=1e7+5;

int sh[maxp],sum[maxp],rank[maxn*320],nc[maxn*320],u[maxn*320],v[maxn*320];
int cc[maxn*320],fa[maxn];
int a[maxn];
int i,j,k,l,n,m,x,y,z,now,ans,hi;

int getfather(int x){
    if (fa[x]==x) return x; else fa[x]=getfather(fa[x]);
    return fa[x];
}
int main(){
    freopen("autosadism.in","r",stdin);
    freopen("autosadism.out","w",stdout);
    scanf("%d",&n);
    m=0;
    fo(i,1,n){
        scanf("%d",&x);
        if (sh[x]==0){
            sh[x]=1;
            a[++m]=x;
        }
    }
    memset(sh,0,sizeof(sh));
    n=m;
    sort(a+1,a+n+1);
    now=n;
    fo1(i,a[n],1){
        while (now>0 && a[now-1]>=i) now--;
        sh[i]=now;
    }
    now=0;
    fo(i,1,n-1){
        j=a[i];
        nc[++now]=a[sh[j+1]]%a[i]; u[now]=i; v[now]=sh[j+1];
        hi=sh[j+1];
        j=j+a[i];
        while (j<=a[n]){
            if (sh[j]==0) break;
            if (hi!=sh[j]){
                nc[++now]=a[sh[j]]%a[i]; u[now]=i; v[now]=sh[j];
                hi=sh[j];
            }
            j=j+a[i];
        }
    }
    fo(i,1,now) sum[nc[i]]++;
    fo(i,1,a[n]) sum[i]+=sum[i-1];
    fo(i,1,now) rank[i]=sum[nc[i]]--;
    fo(i,1,now) cc[rank[i]]=i;
    z=0;
    fo(i,1,n) fa[i]=i;
    fo(i,1,now){
        x=getfather(u[cc[i]]); y=getfather(v[cc[i]]);
        if (x==y) continue;
        z++;
        if (z>=n) break;
        ans=ans+nc[cc[i]];
        fa[x]=y;
    }
    printf("%d\n",ans);
    return 0;
}

你可能感兴趣的:(不知道怎么分类啊)