见证者为见证而来,铭记者因铭记而生|

园龄:粉丝:关注:

[CF2048D] Kevin and Competition Memories 题解

传送门

法一

提供一种非常 naive 且暴力的思路。
小于等于 Kevin 的选手没有任何贡献,扣掉。
我们发现,我们如果按轮次一个个填充这一堆比赛,实际上是可以确定一个(可能的)最优填充次序的。
先填 Kevin 会做的。这些题目不影响排名
再按 bi 从大到小依次填充。这样是最优的,因为 bi 越小,贡献越大,肯定是按顺序填充的,要么从大到小,要么从小到大。显然降序更优。(感性理解)
做完了。

Q:不会 T 吗?
A:我们填充时,只关心一场比赛中,除 Kevin 会做的比赛外,最简单的题目。于是 ST 表维护即可。而对于一个 k,只有 mk 场比赛。所以比赛总场数是 k=1mmk=O(mlogm) 的。带上二分,总时间复杂度 O(mlog2m)

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define Linf 0x3f3f3f3f3f3f3f3f
#define pii pair<int, int> 
#define all(v) v.begin(), v.end()
using namespace std;

//#define filename "xxx" 
#define FileOperations() freopen(filename".in", "r", stdin), freopen(filename".out", "w", stdout)


namespace Traveller {
	const int N = 3e5+2;
	
	int n, m, x, tmp[N], *a, b[N];
	vector<int> c;
	
	int f[N][20], LOG2[N];	//ST表
	int query(int l, int r) {
		if(l > r) return inf;
		int k = LOG2[r - l + 1];
		return min(f[l][k], f[r - (1 << k) + 1][k]);
	}
	
	void main() {
		cin >> n >> m;
		for(int i = 1; i <= n; ++i) scanf("%d", &tmp[i]);
		x = tmp[1], a = tmp + 1;
		for(int i = 1; i <= m; ++i) scanf("%d", &b[i]);
		
		sort(a+1, a+n);
		a = upper_bound(a+1, a+n, x) - 1;
		n -= a - tmp;
		sort(b+1, b+m+1);
		
		c.clear();
		c.push_back(0);
		int easy = 0;
		for(int i = 1; i <= m; ++i)
			if(b[i] <= x) c.push_back(b[i]), ++easy;
			else break;
		for(int i = m; i >= 1; --i)
			if(b[i] > x) c.push_back(b[i]);
			else break;
		//按c数组的顺序依次填充比赛
		
		for(int i = 2; i <= m; ++i) LOG2[i] = LOG2[i >> 1] + 1;
		for(int i = 1; i <= m; ++i) f[i][0] = c[i];
		for(int k = 1; k <= 19; ++k)
			for(int i = 1; i + (1 << k) - 1 <= m; ++i)
				f[i][k] = min(f[i][k-1], f[i + (1 << k-1)][k-1]);
		
		for(int k = 1; k <= m; ++k) {
			int p = 1;
			long long ans = 0;
			for(int r = 1; r <= m / k; ++r) {
				int v = query(max(p, easy+1), p+k-1);
				int rank = n - (lower_bound(a+1, a+n+1, v) - a) + 2;
				ans += rank;
				p += k;
			}
			printf("%lld ", ans);
		}
		puts("");
	}
}

signed main() {
	#ifdef filename
		FileOperations();
	#endif
	
	int _;
	cin >> _;
	while(_--) Traveller::main();
	return 0;
}


法二

延续前面的观察。
我们处理出 ci,表示能做出第 i 题的人数。
于是排名就转化成了连续 kci 的最大值和。
对于 a1 的题目,直接按照 ci 升序排列,挨个求就好了。啥手法都不需要,时间复杂度调和级数。
总时间复杂度 O(nlogn)

posted @   wfc284  阅读(28)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起