组合数学
对每个数,先分类讨论,将答案分为这个数有翻倍和这个数没翻倍。
在讨论前,我们规定:(low(x))为小于等于这个数的数的个数,当前数为(x)。
subtask1
如果这个数没翻倍:我们考虑哪些数翻倍不会影响这个数的排名,一种是翻倍后依然小于(x)的,一种是本来就大于等于(x)的。
那么对于第一种情况,情况数为
[C(low((x+1)/2-1)+n-low(x-1)-1,k)
]
((C(n,m))为组合数,下同)
这里前面一项最后的减一表示不能选当前数。
subtask2
如果这个数翻倍了:我们考虑那些数必须翻倍,那些数可以选择性翻倍。
必须翻倍的数就是为了维持原排名所需翻倍的数,就是小于(2x)同时大于等于(x)的所有数。
选择翻倍的数就是翻倍后不会影响当前排名的数,就是小于(x)的数和大于等于(2x)的数的数。
这里要注意,如果必须翻倍的数大于(k)那么在这种讨论中就不可能有情况满足当前数排名不变。
设必须翻倍的数为(js=low(2x-1)-low(x-1)),那么对于第二种情况,情况数为
[C(n-js,k-js)
]
那么答案就是两者相加了。
#include<bits/stdc++.h>
using namespace std;
const int N=100010,p=998244353;
int add(int x,int y){
return (x+y)%p;
}
int mul(int x,int y){
return 1ll*x*y%p;
}
int n,k;
int a[N],b[N];
int fac[N],inv[N];
int C(int n,int m){
return mul(fac[n],mul(inv[m],inv[n-m]));
}
int mpow(int a,int n){
int ret=1;
while(n){
if(n&1)ret=mul(ret,a);
a=mul(a,a);
n/=2;
}
return ret;
}
int low(int x){
return (upper_bound(b,b+n+1,x)-b-1);
}
int main(){
inv[0]=fac[0]=1;
for(int i=1;i<N;++i)fac[i]=mul(fac[i-1],i);
inv[N-1]=mpow(fac[N-1],p-2);
for(int i=N-2;i;--i)inv[i]=mul(inv[i+1],i+1);
cin>>n>>k;
for(int i=1;i<=n;++i){
scanf("%d",a+i);
b[i]=a[i];
}
sort(b+1,b+n+1);
b[0]=-1e9+7;
b[n+1]=0x7fffffff;
for(int i=1;i<=n;++i){
if(a[i]==0){
printf("%d
",C(n,k));
continue;
}
int ret=C(low((a[i]+1)/2-1)+n-low(a[i]-1)-1,k);
int js=low(2*a[i]-1)-low(a[i]-1);
if(k>=js){
ret=add(ret,C(n-js,k-js));
}
printf("%d
",ret);
}
}