2020 CCPC 秦皇岛 E.Exam Results

我搬运我自己应该算原创吧

题目

题意

T T T组测试. n n n个学生参加考试. 如果第 i i i个学生发挥得好, 他可以得 a i a_i ai分; 否则他只能得 b i b_i bi分. 一场考试结束后, 设最高分为 x x x, 及格线为 x ⋅ p x \cdot p xp%. 问可能最多有几个学生及格.

1 ≤ T ≤ 5 ⋅ 1 0 3 , 1 ≤ n ≤ 2 ⋅ 1 0 5 , 1 ≤ p ≤ 100 , 1 ≤ b i ≤ a i ≤ 1 0 9 1 \le T \le 5 \cdot 10^3, 1 \le n \le 2 \cdot 10^5, 1 \le p \le 100, 1 \le b_i \le a_i \le 10^9 1T5103,1n2105,1p100,1biai109

题解

枚举最高分的时候, 应该对分数进行从大到小的顺序排序, 然后依次考虑, 考虑过的分数是不能选的.

那么既然要枚举最高分, 那么一定得把 n n n个学生"拆"开, 一共 2 n 2n 2n个分数. 这样才可以枚举最高分.

每个学生都至少需要一个分数, 所以一但在枚举的过程中发现同一个学生的两个分数都枚举过了, 那么直接结束, 即枚举完第一个出现的 b b b结束.

我们需要维护 [ i , p o s ] [i, pos] [i,pos]中学生的个数. 注意到 x x x x ⋅ p x \cdot p xp% 都是单调的, 那么只需要维护一个滑动窗口即可. 具体来说: 维护一个数组 vis[] , 表示学生在窗口内出现的次数. 和 tot , 表示窗口内学生总数.

  1. 尾指针拓展一个分数时, 找到对应的学生, 记录次数( vis[tail]++ ). 如果是第一次找到这学生( 修改前的vis[tail] == 0 ), 则窗口内学生总数加一( tot++ ). 不断拓展尾指针, 直到他不在及格线内( score[tail] < score[head] * p / 100 ).
  2. 头指针移除时, 找到对应学生, 减去次数( vis[head]-- ), 同理维护 tot .

复杂度 O ( n ) O(n) O(n)

最后注意score[head] * p / 100, a ( b ) a(b) a(b) 最大可为 1 0 9 10^9 109, p p p最大为 100 100 100, 直接乘就炸int啦!!!

我才不会告诉你因为这个细节vp时调了3个小时换了2种做法WA了9发, 而且当场还没一个人找到这个极其**的失误

{{% code %}}

const int maxn = 2e5+10;

int t, n, p;
int a[maxn], b[maxn], vis[maxn];

struct Score {
	int num, flag, id;
	bool operator < (const Score &N) const {
		return num == N.num ? flag < N.flag : num > N.num;
	}
} score[maxn * 2];

int main() {
	scanf("%d", &t);
	for (int kase = 1; kase <= t; kase++) {
		scanf("%d%d", &n, &p);
		int ans = 0;
		for (int i = 1; i <= n; i++) {
			scanf("%d%d", a + i, b + i);
			score[i] = Score{a[i], 0, i};
			score[i + n] = Score{b[i], 1, i};
			vis[i] = 0;
		}
		sort(score + 1, score + 1 + 2 * n);

		int tot = 0, tail = 1;
		for (int head = 1; head <= 2 * n; head++) {
			while (score[tail].num >= score[head].num * (p / 100.0)) {
				int id = score[tail].id;
				if (vis[id] == 0)
					tot++;
				vis[id]++;
				tail++;
			}
			ans = max(ans, tot);
			if (score[head].flag)
				break;
			int id = score[head].id;
			if (vis[id] == 1)
				tot--;
			vis[id]--;
		}
		printf("Case #%d: %d\n", kase, ans);
	}
	return 0;
}

{{% /code %}}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值