【杭电】Medians Strike Back 题解

传送门:Medians Strike Back
标签:构造

题目大意

规定一个长度为n的序列的中位数为:1、n为奇数时为序列排序后第(n+1)/2位;2、n为偶数时为第n/2和n/2+1位中出现次数较多的那个,若出现次数相同则为较小的那个。再规定一个序列的shikness为该序列中位数的出现次数,一个序列的nitness为该序列所有子段的shikness的最大值。现在给出一个正整数n,需要你构造一个序列a1,a2,······,an,满足1<=ai<=3且该序列的nitness最小。
输入:T组数据,每行一个正整数n(1<=n<=1e9),代表序列长度。
输出:一个正整数,代表nitness的最小值。

算法分析

  • 看n的数据范围就知道这题要么O(1)要么O(logn),先用高复杂度的O(3n)算法打表看看有无规律,实践后发现没有普遍公式,那么我们就想O(logn)的做法。带log的算法有很多,但考虑到空间复杂度的限制和数据范围,这题应该是用二分。那我们距离通过本题就差临门一脚了,只要知道二分用在哪里即可。
  • 先思考shikness最小的情况。排序后1在2前面,2在3前面,这是必然的,如果让1或者3作为中位数,那么shikness的值至少为n/2,这样显然不是最优的。我们如果让2在序列的最中间,即n为奇数时a(n+1)/2=2,n为偶数时an/2=an/2+1=2,然后让2前面全是1,2后面全是3,就能将序列的shikness控制在区间[1,2]之间,毫无疑问没有更优的策略。
  • 想让shikness最小很简单,但nitness的情况就复杂很多了。它涉及到每个子段的shikness,那我们的思路不变,还是尽量保证2在最中间,你可能会想到123无限循环的构造,这样一来答案虽然稳定,但会变成n/3。我们不需要保证任意一个子段都是最优的情况,只要保证其shikness小于整个序列的shikness就行了。
  • 我们假设这是一场博弈,对手要从我们构造的序列中找到最劣的一段。我们上面已经分析过,最优构造下1和3的数量相差不超过一,且n为奇时需要一个2,n为偶时需要两个2。如果完整的序列满足条件,那么对手肯定会选择一个不含2的最长子段或者只含一个2的最长偶数段。为了让最劣子段尽量满足条件,我们先保证其1和3的数量尽可能接近,即构造出13131313循环的序列。然后我们再考虑把一些2插进去,假设我们每隔k个数插一个2,那么对手肯定会选择一个长度为2k的只包含一个2的子段,使得shikness为k。
  • 对于一个13 2 13 2 13的序列,我们发现两个2将原序列分成了三段,中间那段对左右两段做的贡献都为k,也就是2k。我们如果要优化,就会把中间的部分向两边分散,使其贡献减小。那么最优情况就变成了原本相差k的两个2最后相邻了。这样一来就得到了正解:每两个2捆在一起作为一个节点插入13序列中。假设每两个节点间相差k个数,那么子段shikness最大为k/2。因为要保证整个序列的nitness等于shikness,也就是2的总个数,所以我们只要让k/2小于等于2的数量即可。根据这一点对2的个数进行二分,就能实现O(logn)的做法。

代码实现

#include <iostream>
using namespace std;
#include <cmath>
#include <algorithm>
#include <map>
#define lowbit(x) x&-x
int n,ans,a[1001],c[1001],cnt[5];
int mi=1e9;

int main(){
	long long i,j,a,b,z,l,r,x,y,mid,k,m,T,cnt,len;
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>T;
	while(T--){
		cin>>n;
		l=1;r=n;
		while(l<r){
			mid=(l+r)>>1;
			x=n-mid;
			k=mid/2;
			y=x/(k+1);
			z=x%(k+1);
			if(z)y++;
			y=(y+1)/2;
			if(y>mid)l=mid+1;
			else r=mid;
		}
		cout<<l<<'\n';
	}
	return 0; 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值