【AT2434】JOI 公園 (JOI Park) 最短路+贪心

题解

我的歪解

我首先想的是分治,我想二分肯定不行,因为它是没有单调性的。

我想了一下感觉它的大部分数据应该是有凸性的(例如\(y=x^2\)的函数图像),所以可以三分。

下面是我的三分代码(骗了不少分)

三分模板没过的我居然瞎歪歪了一个三分

歪解code:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cctype>
#include<queue>
#define ll long long
#define R register
#define N 400005
#define INF 0x7fffffffffffLL
using namespace std;
template<typename T>inline void read(T &a){
    char c=getchar();T x=0,f=1;
    while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
    a=f*x;
}
ll n,m,c,tot,h[N],vis[N],pd[N];
ll dist[N],sum,now_ans,now;
struct bian{
    int u,v;
    ll w;
}b[N];
struct node{
    int nex,to;
    ll dis;
}edge[N<<1];
inline void add(R int u,R int v,R ll w){
    edge[++tot].nex=h[u];
    edge[tot].to=v;
    edge[tot].dis=w;
    h[u]=tot;
}
inline void spfa(R int s){
    for(R int i=1;i<=n;i++)dist[i]=INF;
    queue<int> q;q.push(s);dist[s]=0;vis[s]=1;
    while(!q.empty()){
        R int x=q.front();q.pop();vis[x]=0;
        for(R int i=h[x];i;i=edge[i].nex){
            R int xx=edge[i].to;
            if(dist[xx]>dist[x]+edge[i].dis){
                dist[xx]=dist[x]+edge[i].dis;
                if(!vis[xx]){
                    vis[xx]=1;
                    q.push(xx);
                }
            }
        }   
    }
}
inline ll check(R ll mid){
    ll tot=0;
    for(R int i=1;i<=n;i++)pd[i]=0;
    for(R int i=1;i<=n;i++)
        if(dist[i]<=mid)pd[i]=1;
    for(R int i=1;i<=m;i++)
        if(pd[b[i].u]&&pd[b[i].v])
            tot+=b[i].w;
    return tot-mid*c;//这是你能节省的
}
int main(){ 
    read(n);read(m);read(c);
    for(R int i=1;i<=m;i++){
        read(b[i].u);read(b[i].v);read(b[i].w);
        add(b[i].u,b[i].v,b[i].w);add(b[i].v,b[i].u,b[i].w);sum+=b[i].w;
    }
    spfa(1);
    R ll l=0,r=sum;
    while(l<=r){
        R ll tmp=(r-l)/3;
        R ll mid1=l+tmp;
        R ll mid2=r-tmp;
        if(check(mid1)>check(mid2)) r=mid2-1;
        else l=mid1+1;
    }
    ll tmp=check(l),temp=check(r);
    if(tmp>temp)now=l,now_ans=tmp;
    else now=r,now_ans=temp;
    printf("%lld\n",sum-now_ans);
    return 0;
}

当然了,三分本来就是一个非常好的骗分算法(也会是正解),有些题在加一些暴力,一定会有神奇的效果;

讲课老师说加上暴力这道题应该可以\(A\)掉,但懒惰的我并没有去实践,有兴趣的可以试一试;

正解

这其实是一道经典的最短路的一种题型。

先跑一遍\(SPFA\),处理出\(dist\)数组;

然后再利用\(dist\)数组处理出每一条边的\(maxdis\);

\(maxdis\)数组从小到大排序(结构体排序);

img

看完图应该都懂了吧。

code:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cctype>
#include<queue>
#define ll long long
#define R register
#define N 800005
#define int long long
#define INF 9999999999999999LL
using namespace std;
template<typename T>inline void read(T &a){
    char c=getchar();T x=0,f=1;
    while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
    a=f*x;
}
ll n,m,c,tot,h[N],vis[N],pd[N],maxdis[N];
ll dist[N],sum,ans,maxsum;
struct bian{
    int u,v,w; 
}b[N];
struct node{
    int nex,to,dis;
}edge[N<<1];
struct MAX{
    int maxdis,id;
    friend bool operator < (const MAX &a,const MAX &b){
        return a.maxdis<b.maxdis;
    }
}md[N];
inline void add(R int u,R int v,R int w){
    edge[++tot].nex=h[u];
    edge[tot].to=v;
    edge[tot].dis=w;
    h[u]=tot;
}
inline void spfa(R int s){
    for(R int i=1;i<=n;i++)dist[i]=INF;
    queue<int> q;q.push(s);dist[s]=0;vis[s]=1;
    while(!q.empty()){
        R int x=q.front();q.pop();vis[x]=0;
        for(R int i=h[x];i;i=edge[i].nex){
            R int xx=edge[i].to;
            if(dist[xx]>dist[x]+edge[i].dis){
                dist[xx]=dist[x]+edge[i].dis;
                if(!vis[xx]){
                    vis[xx]=1;
                    q.push(xx);
                }
            }
        }   
    }
}
signed main(){ 
    read(n);read(m);read(c);
    for(R int i=1;i<=m;i++){
        read(b[i].u);read(b[i].v);read(b[i].w);
        add(b[i].u,b[i].v,b[i].w);add(b[i].v,b[i].u,b[i].w);sum+=b[i].w;
    }
    spfa(1);
    for(R int i=1;i<=m;i++)
        md[i].maxdis=max(dist[b[i].u],dist[b[i].v]),md[i].id=i;
    sort(md+1,md+1+m);
    ans=sum;
    for(R int i=1;i<=m;i++){
        sum-=b[md[i].id].w;
        ans=min(ans,1LL*md[i].maxdis*c+sum);
    }
    printf("%lld\n",ans);
    return 0;
}

转载于:https://www.cnblogs.com/ZAGER/p/9817918.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值