BZOJ_5368_[Pkusc2018]真实排名_组合数

BZOJ_5368_[Pkusc2018]真实排名_组合数

Description

小C是某知名比赛的组织者,该比赛一共有n名选手参加,每个选手的成绩是一个非负整数,定义一个选手的排名是:成绩不小于他的选手的数量(包括他自己)。例如如果333位选手的成绩分别是[1,2,2],那么他们的排名分别是[3,2,2]。拥有上帝视角的你知道所有选手的实力,所以在考试前就精准地估计了每个人的成绩,设你估计的第iii个选手的成绩为Ai,且由于你是上帝视角,所以如果不发生任何意外的话,你估计的成绩就是选手的最终成绩。但是在比赛当天发生了不可抗的事故(例如遭受到了外星人的攻击),导致有一些选手的成绩变成了最终成绩的两倍,即便是有上帝视角的你也不知道具体是哪些选手的成绩翻倍了,唯一知道的信息是这样的选手恰好有k个。现在你需要计算,经过了不可抗事故后,对于第i位选手,有多少种情况满足他的排名没有改变。由于答案可能过大,所以你只需要输出答案对998244353取模的值即可。

Input

第一行两个正整数n,k
第二行n个非负整数A1..An
1≤k<n≤10^5 ,0≤Ai≤10^9

Output

输出n行,第i行一个非负整数ansi,表示经过不可抗事故后,第i位选手的排名没有发生改变的情况数。

Sample Input

3 2
1 2 3

Sample Output

3
1
2
样例解释
一共有3种情况:(1,2)翻倍,(1,3)翻倍,(2,3)翻倍。
对于第一个选手来说,他的成绩就算翻倍,其他人都不低于他,所以任意情况下他的排名都不会改变。
对于第二个选手来说,如果是(1,2)翻倍,成绩变成(2,4,3),他的排名变成了第一;
如果是(1,3)翻倍,则成绩变成(2,2,6),他的排名变成了第三;如果是(2,3)翻倍,则成绩变成(1,4,6),他的排名还是第二。
所以只有一种情况。
对于第三个选手来说,如果是(1,2)翻倍,他的排名会变成第二,其他情况下都还是第一。


 

GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG

调了一晚上!!!最后发现组合数求错了!!没判m<0的情况!!那应该RE啊怎么一直WA啊...

求出每个人的排名rank然后分这个人是否乘2进行讨论,设这个人的分数为w。

每次求出 原来小于w,乘2后也小于w的个数p1.原来大于等于w,乘2后也大于等于w的个数p3,其他的人数p2。设第三种人选了q2个人。

有p3+q2=rank-1

那么答案为C(p2)(q2)*C(p1+p3)(K-q2)或C(p2)(q2)*C(p1+p3)(K-q2-1)。

注意当a[i]=0时直接令答案为C(n)(K)即可,省的在求p3的时候特判。

 

代码:

#include <cstdio>
#include <string.h>
#include <algorithm>
using namespace std;
typedef long long ll;
#define mod 998244353
#define N 100050
int n,K;
ll fac[N<<1],inv[N<<1],ans[N];
int t1[N],t2[N],p1,p2,p3,Rank[N],a[N];
ll qp(ll x,ll y) {
    ll re=1; for(;y;y>>=1ll,x=x*x%mod) if(y&1ll) re=re*x%mod; return re;
}
void init() {
    int i;
    for(fac[0]=1,i=1;i<=n+n;i++) fac[i]=fac[i-1]*i%mod;
    inv[n+n]=qp(fac[n+n],mod-2);
    for(i=n+n-1;i>=0;i--) inv[i]=inv[i+1]*(i+1)%mod;
}
ll C(ll n,ll m) {
    if(m<0||n<m) return 0;
    return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int main() {
    scanf("%d%d",&n,&K);
    init();
    int i;
    for(i=1;i<=n;i++) {
        scanf("%d",&a[i]); t1[i]=a[i]; t2[i]=a[i]<<1;
    }
    sort(t1+1,t1+n+1);
    sort(t2+1,t2+n+1);
    for(i=1;i<=n;i++) {
        Rank[i]=n-(lower_bound(t1+1,t1+n+1,a[i])-t1)+1;
    }
    for(i=1;i<=n;i++) {
        if(a[i]==0) {ans[i]=C(n,K); continue;}
        p1=lower_bound(t2+1,t2+n+1,a[i])-t2-1;
        p3=n-(lower_bound(t1+1,t1+n+1,a[i])-t1);
        p2=n-1-p1-p3;
        int q2=Rank[i]-1-p3;
        ans[i]=C(p2,q2)*C(n-1-p2,K-q2)%mod; 
    }
    for(i=1;i<=n;i++) {
        if(a[i]==0) continue;
        p1=lower_bound(t2+1,t2+n+1,a[i]<<1)-t2-1;
        p3=n-(lower_bound(t1+1,t1+n+1,a[i]<<1)-t1)+1;
        p2=n-1-p1-p3;
        int q2=Rank[i]-1-p3;
        ans[i]=(ans[i]+C(p2,q2)*C(n-1-p2,K-q2-1)%mod)%mod;
    }
    for(i=1;i<=n;i++) printf("%lld\n",ans[i]);
}

 

转载于:https://www.cnblogs.com/suika/p/9158396.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值