[JZOJ5445]失格

题目大意

给你n个点,每个点有权值 pi ,一个点x和y连边的费用是 min(px%py,py%px) ,求原图最小生成树的费用。
n<=100000, pi<=107 ,时限7秒开O2。

分析

这道题乍地一看只会 O(n2) 的做法,而一般这种题就是优化边数然后用经典做法。
研究一下边费用的特征,发现肯定是大的模小的更小。两个p相同的点,直接并成1个点即可。
考虑p的值域只有 107 ,我们尝试有关倍数的方法。
对于一个点x,我考虑他跟p更大的连边,大胆尝试把最接近 px+1,2px,3px... 的点和他的边都记录下来,然后连起来。然后就会发现这样连边不会漏掉最优解的任何一条边。
如何证明呢?考虑 apx(a+1)px 之间的那些点,假如从小到大为 b1,b2...bm 。我们没有必要让x和除了 b1 的其他点连。
反证法:我们给x和 b1bk 都连。按照刚刚的方法b之间两两都有连边。那么x和 bk 的边费用是 pbk%px 的,然而让x和b1,b1和b2…b_k-1和b_k都连边的费用也是这么大,这条边显然没什么用。
正确性证完了,我们分析一下这样的边数多少。弄一个松一点的上界: ni=1107/i=107ni=11/i107×ln(n) 。实际上是远远达不到这个大小的,放心地开够空间限制大小的边集数组,排序用桶排,或者直接插进链表里,就可以过了。

代码

#include<cstdio> 
#include<algorithm>
#include<cstring>
#include<cmath>
#include<set>
using namespace std;
typedef long long ll;
typedef double db;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
#define cmin(a,b) (a=(a<b)?a:b)
#define cmax(a,b) (a=(a>b)?a:b)
const int N=1e5+5,M=1e7+2e6+5;
struct rec
{
    int x,y;
}b[M*3];
int a[N],fa[N],i,j,tb,n,ri[M],mx,pd[M],tmp,lst,x,fst[M],nxt[M*3],b1[M*3],p,tt,k;
ll ans;
void cr(int x,int y)
{
    tt++;
    b1[tt]=y;
    nxt[tt]=fst[x];
    fst[x]=tt;
}
int get(int x)
{
    if (fa[x]==x) return x;
    return fa[x]=get(fa[x]);
}
int main()
{
    freopen("t3.in","r",stdin);
//  freopen("autosadism.out","w",stdout);
    scanf("%d",&n);
    fo(i,1,n)
    {
        scanf("%d",a+i);
        cmax(mx,a[i]);
        pd[a[i]]=i;
    } 
    fd(i,mx,1)
    {
        if (pd[i]) tmp=i;
        ri[i]=tmp;
    }
    fo(i,1,mx)
    {
        if (pd[i])
        {
            lst=0;
            for (j=i;j<=mx;j+=i)
            {
                x=ri[j+(i==j)];
                if (x!=lst)
                {
                    cr(x%i,++tb);
                    b[tb].x=pd[i];
                    b[tb].y=pd[x];
                    lst=x;
                }
            }
        }
    }
    fo(i,1,n) fa[i]=i;
    fo(k,0,mx)
    {
        for(p=fst[k];p;p=nxt[p])
        {
            i=b1[p];
            if (get(b[i].x)!=get(b[i].y))
            {
                ans+=k;
                fa[get(b[i].x)]=get(b[i].y);
            }
        }
    }
    printf("%lld",ans);
} 
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下 4载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下载 4使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下载 4使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值