2023 杭电多校第七场


题意

构造出一个子序列中位数出现次数最大最小的 仅由1,2,3组成的序列,并输出这个最小值

思路

求中位数问题,并且仅由三个数组成序列,那么中位数就肯定是这三个数之一了,怎么让中位数出现的次数最小呢,而且是所有子序列中的中位数出现次数的最大值 最小

1. 当n=3时肯定是1,序列为[123]

2.当n=4时 答案肯定是2了,因为四个数中肯定会有一个数重复一次,可以是[1223]

3.当n=5时 答案肯定是2了?  [11233],因为子序列可以取[11],或者[33]   

4.当n=7时, 这时候均分的话已经有一个数出现3次了,但3不是最小的,2才是,这时候就要考虑下怎么排版了,[1231231]就可以

5.当n=8时,继续[12312312],答案是3 但3是最小的吗?我们发现这时候的3是2当中位数的情况,进一步思考一下,结合只由三个数构成,2是中间的数,是不是我们保证1,3一样多中位数就一定是2的个数呢?那么怎么保证1,3一样多呢 ? 我们可以将13绑定2放在最后,这样当有奇数个数肯定2就是中位数比如[13132],然后2放中间似乎更合理[13213],所以当n=5,答案1,那么当n=8是偶数个的时候呢,那就放两个2[132213]答案就是2

6. 再进一步思考 这个序列中2的作用究竟是什么? 不难发现横跨2的区间中位数一定是2,比如[32213],子序列长度为奇数个时,要么3多一个,要么1多一个,这时候2个2中的一个就可以去弥补少的那一个从而2成为中位数,答案就是2的个数,当不包含2时[1313]答案即是[13]重复的次数,那么我们发现由[22]分隔的一个序列[1313221313]答案就是[13]重复的次数2,那么我们是不是可以把答案为x的序列表示为x*[13] + [22]   ,但是长度为x*[1,3]+[22]的序列答案一定是x吗?显然不是,我们发现x可以尽可能小一些,变成x/2 * [13] + [22] + x/2 * [13],这样答案就是x/2了,再分分试试,x/3 *[13] + [22] + x/3 *[13] + [22] + (x/3 *[13] - [22]) 最后一个可能凑不完整,我们发现只要有[22]隔着他们,他们子序列中位数最大就是[13]在这个序列出现次数,除非什么呢?除非代码整体2的个数大于单个序列[13]的长度 比如[1313221313221313]虽然[13]在子序列中只出现2次,但2个数时4,当序列包括2时,中位数肯定是2的个数,也就是整体的中位数个数肯定是2的个数,这时候就不满足了,但是好像发现了一个二段性! 一个长度为n的它可以答案等于2那么一定可以等于3,少几个2而已,这时候我们就可以用二分来确定一个长度为n的序列的答案,就是判断答案为x时出现2的个数是否大于x,大于x的话x就不是答案,小于等于的话x就有可能时答案,这不就是单调性么, 上二分代码!

链接

题目链接

CODE

#include
#define endl "\n"
using namespace std;
const int N = 1e5 + 10;
const int mod = 998244353;

typedef long long LL;
//找出长度为n 连续的A的子序列 并且中位数出现次数最少,并且求出最小中位数次数

void solve()
{
	LL n; cin >> n;
	function< bool(LL)> check = [&](LL x)
	{
		//1313 22 1313 22 1313
		LL r = n/(2*x+2) * 2 + max(0ll, n%(2*x+2)-2*x);
		// n包含[13]*x + [22] 个数  也就是去掉前面整除的[13]*x + [22]后的区间包含2的个数
		return r <= x;
	};
	int l = 1, r = n;
	while(l < r)
	{
		int mid = (l + r) >> 1;
		if(check(mid))
		{
			r = mid;
		}
		else
			l = mid+1;
	}
	cout << l << endl;
}

int main()
{
	ios::sync_with_stdio(false); cin.tie(0);
	int T; cin >> T;
	while(T --) solve();
	return 0;
}

你可能感兴趣的:(多校训练,算法)