CF1503C旅行商问题

首先,我们进行第一步的转换。
考虑到所有c必须被取过一次,所以我们把这个数值减掉,最小化额外的花费。
另外,由于旅行商走到是一个环,其实不用管他从哪里开始的,从最低点爬到最高点(指a最小和a最大),再从最高点免费滑下来就行了。
(这题可以形象的想象成登山,c为一个站点免费为你提升的缆车长度…)

官方给出的第一个题解是dijkstra,很好理解,很好证明。然后洛谷上有人说可以通过c[i]范围的确定能扩展到那些结点,然后设为可以访问,直到访问到最高处(美丽度最大的城市)。详见其他题解。

官方题解2就要简单得多。直接统计所有结点,拿a[i]减去最大的a[j]+c[j]即可。会发现,这个做法也是正确的。
想象有n个城市,有一些可以不用费用就到达,成为几个连通块。连通块之间需要费用。对于两个相邻的连通块,如果把上一个连通块的所有结点到下一个连通块的所有结点都两两计算一遍,肯定是可以得到正确的费用。(但实际上上面有用的只有一个,就是最低的那个)而且我在一个结点上,需要统计的恰好是下面的所有结点中到达我的最小值。然后连通块中更大的结点的贡献都会成为0,因为它是联通快,从最低点可以免费走上去。所以,既然我把全局的结点都两两算出了,就可以让每个结点的最小值相加,得到总贡献。码量减少了一大半。

大量的文字有点抽象,我们看一个图。
有人可能会给出这样一个反例。
图片中,黄色表示a[i]+c[i]。
我现在从1出发,到达了2,3,4,从3出发,可以到达5,好像是联通的。
但是只有3是联通的。你去了3,就去不了2和4了,不满足题意。

我给出的解释是:到达3之后,你可以直接前往5,然后在从5滑下来的路上把2和4带走。

AC代码:

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    ll n,fans,f[110000],nx,mx;
    struct city{
    	ll a,b,c;//忽略这个b,它没有用
    } a[110000];
    bool cmp(city a,city b){
    	if(a.a != b.a)return a.a<b.a;
    	else return a.c>b.c;//这句话不重要,第二关键词不要也行
    }
    int main(){
    	cin >> n;
    	for(ll i=1;i<=n;i++){
    		scanf("%lld%lld",&a[i].a,&a[i].c);
    		a[i].b=i;
    		fans += a[i].c;
    	}
    	sort(a+1,a+n+1,cmp);
    	mx=a[1].a+a[1].c;
    	for(ll i=2;i<=n;i++){
    		fans += max(0ll,a[i].a-mx);
    		mx=max(mx,a[i].a+a[i].c);
    	}
    	cout << fans;
    	return 0;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值