[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;
}
夜畔流离回,暗叹永无殿。
独隐万花翠,空寂亦难迁。
千秋孰能为,明灭常久见。
但得心未碎,踏遍九重天。