程式碼源每日一題 #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]);
}