「CCO 2017」专业网络(线段树、贪心)

Kevin 正在一个社区中开发他的专业网络。不幸的是,他是个外地人,还不认识社区中的任何人。但是他可以与 N 个人建立朋友关系 。

然而,社区里没几个人想与一个外地人交朋友。Kevin 想交朋友的 N 个人都有类似但不同的与外地人交友的准则。在 Kevin 已经直接认识了社区中的 Ai 个人后,第 i 个人就愿意与 Kevin 交朋友了,否则 Kevin 就要付出 Bi 的代价与他成为朋友。

你的任务是,使 Kevin 与这 N 个人都交上朋友,并且最小化他付出的代价。

第一行包含整数 N。接下来的 N 行每行包含两个整数 Ai 和 Bi。

输出一行一个整数表示 Kevin 付出的最小代价。

题解

首先把所有的人按Ai排序的话,这N个人会形成一条折线,

如果一个一个人地解决掉(以强大的人脉折服他或收买他),那你的人脉的增长会成一条直线,

那么一个人 i 若在这个序列的位置为 i' ,且 i' > Ai,则解决这个人是不用花钱的,

这就很像一道叫 “不守交规”的贪心基础题,

描述

     近些年来,生活水平越来越好,私家车也成了很多家庭必备之物。但某些司机总是不守交规,罚单也是接踵而至。

    有一位不遵守交规的司机,在同一天收到了n条违章罚单短信(1≤n≤100),每条罚单短信中有两个内容,一:交罚款的最后剩余时间ti;二:过期未交的滞纳金mi(1≤ti,mi≤1000),假设不管过期多少天,滞纳金数量不会改变,而且,这位司机很忙,每天最多只能处理一张罚单,那么,这位司机应该按怎样的处理违章短信的顺序,才能使滞纳金总和最少?

输入共n+1行

第1行:收到短信数n
后n行:每行分别两个数,最后期限ti和过期滞纳金mi,用空格隔开

输出

最少的滞纳金总和

样例输入

4
1 50
1 100
2 60
3 60

样例输出

50

解题思路

        首先我们能够想到按照滞纳金的从大到小进行排序,但是真的是按照时间顺序处理这些从大到小的滞纳金吗?其实不是的,我们最优贪心思想应该是按照滞纳金的按照滞纳金的从大到小进行排序,越大的滞纳金能够拖到最后一天处理就最后一天处理;如果最后一天已经处理过以其他的滞纳金,则就从这一天倒着往前开始找,看看那一天没有处理过滞纳金,就在这一天处理该滞纳金;如果没有找到,则说明一定要缴纳此滞纳金。

——————————————————————————————————————————
原文链接:https://blog.csdn.net/qq_37220238/article/details/80013402

一样的思路,把人按Bi从大到小排,然后在 [Ai + 1,n]中找一个最靠左的位置放,位置被占完了就只能拿钱买他。

只不过得加一个log优化

#include<cstdio>
#include<cstring>
#include<vector>
#include<stack>
#include<queue>
#include<algorithm>
#include<map>
#include<cmath>
#include<iostream>
#define LL long long
using namespace std;
int read() {
    int f = 1,x = 0;char s = getchar();
    while(s < '0' || s > '9') {if(s == '-')f = -1;s = getchar();}
    while(s >= '0' && s <= '9') {x = x * 10 + s - '0';s = getchar();}
    return x * f;
}
LL max(LL a,LL b) {
	return a > b ? a : b;
}
struct it{
	LL nm,co;
}a[200005];
bool operator < (it a,it b) {
	if(a.co != b.co) return a.co > b.co;
	else return a.nm < b.nm;
}
LL n,m,i,j,k,s,o;
int tre[800005];
int aa[200005];
int bing(int x,int y) {
	if(aa[x] < aa[y]) return x;
	if(aa[x] > aa[y]) return y;
	return x < y ? x : y;
}
void maketree(int n) {
	m = 1;
	while(m < n + 2) m <<= 1;
}
void F5(int x) {
	int s = m + x;
	tre[s] = x;
	s /= 2;
	while(s) {
		tre[s] = bing(tre[s * 2],tre[s * 2 + 1]);
		s /= 2;
	}
	return ;
}
int findt(int l,int r) {
	int s = m + l - 1,t = m + r + 1;
	int ans = 0;
	while(s || t)  {
		if(s / 2 != t / 2) {
			if(s % 2 == 0) {
				ans = bing(ans,tre[s + 1]);
			}
			if(t % 2) {
				ans = bing(ans,tre[t - 1]);
			}
		}
		else break;
		s /= 2;t /= 2;
	}
	return ans;
}
int main() {
	n = read();
	maketree(n + 1);
	for(int i = 1;i <= n;i ++) {
		a[i].nm = read();
		a[i].co = read();
	}
	sort(a + 1,a + 1 + n);a[0].co = 0x7f7f7f7f;
	for(int i = 1;i <= n + 1;i ++) F5(i);aa[0] = 2;
	LL ans = 0;
	for(int i = 1;i <= n;i ++) {
		int j = a[i].nm + 1,k;
//		cout<<j<<endl;
		if((k = findt(j,n + 1)) <= n) {
			aa[k] = 1;
			F5(k);
		}
		else ans += a[i].co;
	}
	printf("%lld\n",ans);
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值