其他分享
首页 > 其他分享> > 代码源每日一题 #618. 选数2

代码源每日一题 #618. 选数2

作者:互联网

#618. 选数2

题目描述

有\(N\)个数, 小t准备在这\(N\)个数中选出若干个.满足这些数的最大值 小于等于 这些数的平均值的 \(k\) 倍.

小t想让自己选的数的个数尽可能多, 试求出有多少数字是不可能被小t选到的.

我们设\(M\)为最多能选出的数的个数, 一个数字不可能被选到 当且仅当不出现在任何一个选出\(M\)个数的方案之中.

输入格式

第一行一个正整数\(N\) 表示数的个数.

接下来一行\(N\)个正整数, 分别表示这\(N\)个数, 两个数字之间用空格隔开.

最后一行两个正整数\(p\)和\(q\), 表示\(k\),(\(k=pq\) 且 \(k>1\)).

输出格式

第一行输出\(M\), 表示不可能被选到的数的个数.

接下来一行输出\(M\)个正整数, 分别表示不可能被选到的数字在原序列中的下标, 并按升序排序. 两个数字之间用空格隔开.

数据范围

对于所有数据, 满足\(1≤n≤2⋅10^5\), \(0≤a_i≤10^9\), \(1≤q<p≤1000\).

提示

有些做法看起来很对, 但是实际上是不太对的. 感觉可以尝试证明一下再写.

样例输入1

4
1 2 3 4
3 2

样例输出1

0

样例解释

在样例一中, 我们最多选出3个数字. 而对于任何一个数字, 都存在一个选出3个数字的方案包含它, 于是没有不可能被选到的数字.

样例输入2

5
1 15 2 5 1
2 1

样例输出2

1
2

样例输入3

5
1 2 3 1000 10000
4 3

样例输出3

2
4 5

分析

对数组进行排序处理。

1. 寻找最大值M

如果对于最大值M存在序列条件成立,那么当我们减去最小的值后,最大值不变,平均值变大,M-1也一定成立,因此该条件存在单调性,可以进行二分寻找最大值。

同时我们可以证明,如果存在长度为M的不连续序列成立,那么连续序列也必定成立。固定住最后一个数字,由于不连续数列的平均值相对于连续序列较小,最大值相同,所以连续序列相对于非连续更优(满足条件)。

因此,如果要寻找M,我们需要对于a进行一次遍历,观看前M个数组成的序列是否满足条件。

2. 寻找有哪些数被选上

确定好M后,对于每个a都查找它是否可以为序列末端,如果是,那么移动左端作为一个数字,看看最小能到哪个地方,此处可以二分,由于\(a[i] <= a[j](i<j)\),那么这种选择就有单调性,二分寻找左端即可。

AC CODE

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e6+3;
struct Node{
	ll val;
	int num;
}a[N];
bool cmp(Node x,Node y) {
	return x.val < y.val;
}
ll sum[N];
int n,p,q;
bool check(int m) {
	if(m > n || m < 1)
		return 0;
	for(int i = m ; i <= n ; i++) {
		ll s = sum[i]-sum[i-m];
		if( 1ll*a[i].val*m*q <= 1ll*s*p)
			return 1;
	}
	return 0;
}
int findM() {
	sort(a+1,a+1+n,cmp);
	for(int i = 1 ; i <= n ; i++)
		sum[i] = a[i].val + sum[i-1];

	int l = 1,r = n;
	for(int i = 1 ; i <= 100 ; i++) {
		int m = (l+r) >> 1;
		if(check(m))
			l = m;
		else r = m;
	}
	return (l+r)>>1;
}
int b[N];
void Minus(int m) {
	for(int i = m ; i <= n  ; i++ ) {
		ll s = sum[i]-sum[i-m];
		if( 1ll*a[i].val*m*q <= 1ll*s*p) {
			//枚举左界
			int l = 1,r = i-m+1,mid;
			s -= a[r].val;
			
			for(int j = 1 ; j <= 100 ; j++) {
				mid = (l+r)>>1;
				if(1ll*a[i].val*m*q <= 1ll*(s+a[mid].val)*p)
					r = mid;
				else l = mid+1;
			}
			b[mid]++;
			b[i+1]--;
		}
	}
	for(int i = 1 ; i <= n ; i++)
		b[i] = b[i]+b[i-1];
}
int main() {
	scanf("%d",&n);
	for(int i = 1 ; i <= n ; i++) {
		scanf("%lld",&a[i].val);
		a[i].num = i;
	}
	scanf("%d%d",&p,&q);
	int m = findM();
	Minus(m);
	vector<int>ans;
	for(int i = 1; i <= n ; i++) { 
		if(b[i]) continue;
		ans.push_back(a[i].num);
	}
	printf("%d\n",ans.size());
	sort(ans.begin(),ans.end());
	for(int i = 0 ; i < ans.size() ; i++)
		printf("%d ",ans[i]);
}

标签:618,数字,int,选数,代码,样例,个数,序列,最大值
来源: https://www.cnblogs.com/zhaohanzheng/p/16096167.html