[PKUSC2018]真实排名

[PKUSC2018]真实排名
简直就是凑数题嘛(
首先将 A 从小到大排序,将 i 的排名转为小于 A[i] 的数的个数,显然与原定义等价。
考虑枚举 i。
若不操作 i,则需要找出 k 个操作后仍小于或仍大于的数,比较好解决。
若操作 i,设排名增加了 kong,那么需要找到 kong 个原来小于 \(2\times A[i]\) 而操作后大于等于 \(2\times A[i]\) 的数,并找到 k-kong 个操作后仍小于或仍大于 \(2\times A[i]\) 的数。
可以使用二分实现。
code:

#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
#define ll long long
const int MAXN=1e5+5;
const int Mod=998244353;
int n,k,l[MAXN],r[MAXN];
ll fra[MAXN<<1],ofra[MAXN<<1],ans[MAXN];
struct ren{
	int id;ll v;bool operator<(const ren &a)const{return v<a.v;};
}a[MAXN];
ll C(int n,int m){if(n<m) return 0;return (fra[n]*ofra[m]%Mod)*ofra[n-m]%Mod;}
int ef(int l,int r,int yq,ll val){
	if(l>r) return 0;
	int res=0;
	while(l<=r){
		int mid=(l+r)>>1;
		if(a[mid].v*yq<val) res=mid,l=mid+1;
		else r=mid-1;
	}
	return res;
}
ll ksm(ll a,int b){
	ll res=1;
	while(b){
		if(b&1) res=res*a%Mod;a=a*a%Mod;b>>=1;
	}
	return res;
}
int main(){
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i].v),a[i].id=i;
	sort(a+1,a+1+n);
	for(int i=1;i<=n;i++){
		int j=i;
		while(a[j+1].v==a[i].v) j++;
		for(int k=i;k<=j;k++) l[k]=i,r[k]=j;
		i=j;
	}
	fra[0]=1;for(int i=1;i<=n;i++) fra[i]=fra[i-1]*i%Mod;
	ofra[n]=ksm(fra[n],Mod-2);
	for(int i=n;i;i--) ofra[i-1]=ofra[i]*i%Mod;
	for(int i=1;i<=n;i++){
		if(a[i].v==0){ans[a[i].id]=C(n,k);continue;}
		ll op=0;int wz=ef(1,l[i]-1,2,a[i].v);
		op=(op+C(wz+n-l[i],k))%Mod;
		wz=ef(1,n,2,a[i].v<<1);
		int wz1=ef(1,n,1,a[i].v<<1);
		int kong=wz1-l[i];
		if(k-1>=kong){
			op=(op+C(wz1-wz-1,kong)*C(n-wz1+wz,k-1-kong)%Mod)%Mod;
		}
		ans[a[i].id]=op;
	}
	for(int i=1;i<=n;i++) printf("%lld\n",ans[i]);
	return 0;
}
posted @ 2023-02-20 22:26  StranGePants  阅读(28)  评论(0编辑  收藏  举报