代码源每日一题 #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