[省选联考 2021 A/B 卷] 卡牌游戏

垃圾福建垫底选手来看看这题。
大家怎么都写带 \(log\) 的。
我来说一个线性做法好了。
那么我们考虑枚举 \(k\) 作为翻转完的最小值。
那么构造出一个满足条件的操作,我们在 \(a_i\) 中查询一个最大的位置使 \(a_i < k\) ,那么 \(a_1\)\(a_i\) 都要进行翻转,且 \(b_1 到 b_i > k\),那么这样做的次数是 \(i\) 或者 \(i - 1\)(考虑\(k\)\(b\)且对应的\(a\)在需要翻转的区间里)的。
那么考虑对这个 \(k\) 求出一个最小的最大值\(z\),同样的我们在\(a_i\)中查询一个最小的位置使\(a_i > z\),那么\(a_i\)\(a_n\)都要进行翻转,且 \(b_i\)\(b_n > k\),且 \(b_i\)\(b_n < z\) ,那么这样做的次数是 \(n - i + 1\) 或者 \(n - i\) (考虑 \(z\)\(b\) 且对应的 \(a\) 在需要翻转的区间里)的,两边次数加起来不超过 \(m\)
我们考虑对这几个条件进行分析一下,首先 \(i\) 对于 \(z\) 减小是单调的,由于 \(min\) 只能下降不能上升, \(max\) 只能上升不能下降,那么对于\(z\)下降来说,他的条件会越来越苛刻。
又因为我们从小到大枚举 \(k\) ,用来满足 \(k\) 的次数会单调不降,那么我们发现,对于一个 \(z\) ,他的所有条件即 \(b_i\)\(b_n > k\),且 \(b_i\)\(b_n < z\) ,次数和小于 \(m\) ,在 \(k\) 上升都具有单调性,感性分析一下,在 \(k\) 上升时, \(z\) 具有单调不降的性质。
那么我们只要对 \(k = 1\) 一个 \(log\) 求出对应的 \(z\) ,再进行双指针,就可以做到 \(O(n)\) 了。

upd:发现自己的这个做法挺难写的,应该考虑计算的时候,也扩展到整个序列就好做了。

[省选联考 2021 A/B 卷] 卡牌游戏
#include <bits/stdc++.h>
using namespace std;
struct hehe{
	long long a, num;
	int op;
	bool operator < (hehe b) const
	{
		return a < b.a;
	}
}a[2000001];
bool used[2000001];
int main()
{
	// freopen("card3.in", "r", stdin);
	int n, k;
	cin >> n >> k;
	for(int i = 1; i <= n; i++)
	{
		scanf("%d", &a[i].a);
		a[i].num = i;
		a[i].op = 1;
	}
	for(int i = 1; i <= n; i++)
	{
		scanf("%d", &a[n + i].a);
		a[i + n].num = i;
		a[i].op = 1;
	}
	sort(a + 1, a + n * 2 + 1);
	int l = 0, r = n * 2 + 1, now = 0;
	while(!used[a[l + 1].num] && now + a[l + 1].op <= k) now += a[l + 1].op, used[a[l + 1].num] = 1, l++;
	while(!used[a[r - 1].num] && now + a[r - 1].op <= k) now += a[r - 1].op, used[a[r - 1].num] = 1, r--;
	long long ans = 1000000000000;
	while(l >= 0)
	{
		ans = min(a[r - 1].a - a[l + 1].a, ans);
		used[a[l].num] = 0;
		now -= a[l].op;
		l--;
		while(!used[a[r - 1].num] && now + a[r - 1].op <= k) now += a[r - 1].op, used[a[r - 1].num] = 1, r--;
	}
	cout << ans << endl;
}
posted @ 2021-04-12 20:34  fhq_treap  阅读(165)  评论(0编辑  收藏  举报