AT_abc373_e 的題解

Jerry_heng發表於2024-10-01

(一)

二分套二分。(感覺是一個很麻煩的做法。)

題目問的是讓額外給的票最少,考慮二分答案。

設二分的答案為 \(x\),該候選人原來的得票為 \(v\),想要超過他至少要 \(x+v+1\)

同時用字首和維護區間和。

第一種情況為該候選人在前 \(m\) 個人中,如下圖所示。

logo

綠色箭頭為被討論的人,藍色箭頭表示 \(x+v+1\) 在原陣列中的位置,藍勾的則是最劣情況下超過當前候選人的人。

可以計算出藍勾位置原來的和與最劣下和的差,是否夠分配。

藍色箭頭指向位置可以二分。(其實是我不會 lower_bound。)

第二種情況是當前候選人不在前 \(m\) 位,那麼無腦選前 \(m\) 中小於 \(x+v+1\) 的即可。

其實兩種情況相差不大,但賽時程式碼又臭又長。

特別鳴謝:Daniel234 在賽時的口頭鼓勵,雖然毫無用處,但他與 *1300 鬥智鬥勇始終印象深刻。

(二)

AC 程式碼。

// LUOGU_RID: 179341441
//2024-09-28 20:13:53
#include<bits/stdc++.h>
#define db double
#define pb push_back
#define eb emplace_back
#define fi first
#define se second
#define mkp make_pair
#define pii pair<int,int>
#define int long long
using namespace std;
bool MBE;
inline int read(){
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-f;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f; 
}
const int mxn=2e5+10;
int ans[mxn],s,n,m,k,sum[mxn];
struct node{
	int val,pos;
}a[mxn];
bool cmp(node x,node y){
	return x.val>y.val;
}
int query1(int pos,int x){
	int t=a[pos].val+x+1,p=k-x,ss;
	int l=1,r=m+1,ans=1;
	while(l<=r){
		int mid=(l+r)>>1;
		if(a[mid].val<=t)ans=mid,r=mid-1;
		else l=mid+1;
	}
	if(ans==pos){
		ans++;
		ss=sum[m+1]-sum[ans-1];
		return (k-x)<t*(m-ans+2)-ss;
	}
	ss=sum[m+1]-sum[ans-1]-a[pos].val;
	return (k-x)<t*(m-ans+1)-ss;
}
int query2(int pos,int x){
	if(a[pos].val+x<a[m].val)return 0;
	int l=1,r=m,ans=1,p=a[pos].val+x+1;
	while(l<=r){
		int mid=(l+r)>>1;
		if(a[mid].val<=p)ans=mid,r=mid-1;
		else l=mid+1;
	}
	int ss=sum[m]-sum[ans-1];
	return (k-x)<p*(m-ans+1)-ss;
}
signed main(){
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif
    n=read(),m=read(),k=read();
    if(n==1){
    	puts("0");
    	return 0;
    }
    if(m==n){
    	for(int i=1;i<=n;i++)
    		printf("0 ");
    	return 0;
    }
    for(int i=1;i<=n;i++){
    	a[i]=(node){read(),i};
    	s+=a[i].val;
    }
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;i++)
    	sum[i]=sum[i-1]+a[i].val;
    k-=s;
    for(int i=1;i<=m;i++){
    	int l=0,r=k,res=-1;
    	while(l<=r){
    		int mid=(l+r)>>1;
    		if(query1(i,mid))r=mid-1,res=mid;
    		else l=mid+1;
    	}
    	ans[a[i].pos]=res;
	}
	for(int i=m+1;i<=n;i++){
    	int l=0,r=k,res=-1;
    	while(l<=r){
    		int mid=(l+r)>>1;
    		if(query2(i,mid))r=mid-1,res=mid;
    		else l=mid+1;
    	}
    	ans[a[i].pos]=res;
	}
	for(int i=1;i<=n;i++)
		printf("%lld ",ans[i]);    
	bool MED;
    cerr<<(&MED-&MBE)/1048576.0<<" MB, "<<1000*clock()/CLOCKS_PER_SEC<<" ms\n";
    return 0;
}