[PKUSC2018] 真实排名

\(\text{Problem}:\)[PKUSC2018] 真实排名

\(\text{Solution}:\)

模拟赛里出过这题。

首先对 \(a_{i}\) 排序。考虑对每个 \(i\) 分类讨论:

  • 不选取 \(i\),那么找出一段被选取了会改变 \(i\) 的排名的区间,使得这段区间不能选。
  • 选取 \(i\),那么找出一段必须和 \(i\) 一起选取的区间,剩下位置随便选。

易发现这两个区间都是连续且单调的,可以利用双指针 \(O(n)\) 求出。

两个细节:

  • \(a_{i}=0\),双指针求区间的性质会被破坏,需要特判答案为 \(\binom{n}{k}\)
  • \(a_{i}=a_{j},i\not=j\)。一种方法是只算第一个 \(a_{i}\)。笔者记录排序后连续相同的 \(a_{i}\) 的最后出现位置,在计算时也较为方便。具体实现可参考代码。

总时间复杂度 \(O(n\log n)\),瓶颈在排序上。

\(\text{Code}:\)

#include <bits/stdc++.h>
#pragma GCC optimize(3)
//#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
#define vi vector<int>
#define vpi vector<pair<int,int>>
using namespace std; const int N=200010, Mod=998244353;
inline int read()
{
	int s=0, w=1; ri char ch=getchar();
	while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); }
	while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar();
	return s*w;
}
int n,K,fac[N+5],inv[N+5];
inline int ksc(int x,int p) { int res=1; for(;p;p>>=1, x=1ll*x*x%Mod) if(p&1) res=1ll*res*x%Mod; return res; }
inline int C(int x,int y) { if(x<y||x<0||y<0) return 0; return 1ll*fac[x]*inv[x-y]%Mod*inv[y]%Mod; }
struct Node { int w,id; }a[N];
inline bool cp(Node x,Node y) { return x.w>y.w; }
int pre[N],nxt[N],Ans[N],vis[N];
signed main()
{
	n=read(), K=read();
	fac[0]=1;
	for(ri int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%Mod;
	inv[n]=ksc(fac[n],Mod-2);
	for(ri int i=n;i;i--) inv[i-1]=1ll*inv[i]*i%Mod;
	for(ri int i=1;i<=n;i++) a[i].w=read(), a[i].id=i;
	sort(a+1,a+1+n,cp);
	int lst=1;
	for(ri int i=2;i<=n;i++)
	{
		if(a[i].w!=a[i-1].w)
		{
			for(ri int j=lst;j<i;j++) vis[j]=i-1;
			lst=i;
		}
	}
	for(ri int i=lst;i<=n;i++) vis[i]=n;
	for(ri int i=n,now=n;i;i--)
	{
		while(a[i].w>a[now].w*2 && now) now--;
		nxt[i]=now+1;
	}
	for(ri int i=1,now=1;i<=n;i++)
	{
		while(a[i].w*2<=a[now].w && now<=n) now++;
		pre[i]=now-1;
	}
	for(ri int i=1;i<=n;i++)
	{
		if(!a[i].w) Ans[a[i].id]=C(n,K);
		else Ans[a[i].id]=(C(n-(nxt[i]-i)+(vis[i]-i),K)+C(n-(i-pre[i])-(vis[i]-i),K-(i-pre[i])-(vis[i]-i)))%Mod;
	}
	for(ri int i=1;i<=n;i++) printf("%d\n",Ans[i]);
	return 0;
}
posted @ 2021-05-03 10:18  zkdxl  阅读(71)  评论(2编辑  收藏  举报