ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2022-04-03 16:03:11  阅读:173  来源: 互联网

标签:618 数字 int 选数 代码 样例 个数 序列 最大值


#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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有