1. 程式人生 > 其它 >程式碼源每日一題 #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]);
}