• 【洛谷5368】[PKUSC2018] 真实排名(组合数学)


    点此看题面

    大致题意:(n)个数字,定义一个数的排名为不小于它的数的个数。现要随机将其中(k)个数乘(2),求对于每个数有多少种方案使其排名不变。

    分类讨论

    对于这种题目,我们可以分类讨论一下,假设当前考虑第(i)个数的答案。

    (a_i)不被修改时,因为原先就(ge a_i)的数不可能在修改后(<a_i),所以我们就可以知道:

    • 原先就(ge a_i)的数可以随意修改。
    • 原先(<a_i)的数如果修改后(ge a_i)就不可以修改。

    如果我们将原序列排序,设(t1)满足(2a_{t1}<a_ile 2a_{t1+1}),那么不可以修改的数就有(i-t1)个,而剩下的(n-(i-t1))个数可以任意修改,即方案数为:

    [C_{n-(i-t1)}^k ]

    (a_i)被修改时,因为原先就(<a_i)的数在修改后必然依旧(<a_i),所以我们就可以知道:

    • 原先就(<a_i)的数可以随意修改。
    • 原先就(ge2a_i)的数可以随意修改。
    • 原先(ge a_i)(<2a_i)的数必须修改。

    如果我们将原序列排序,设(t2)满足(a_{t2}<2a_ile a_{t2+1}),那么必须修改的数就有(t2-i+1)个(注意(a_i)必须修改),而剩下的(n-(t2-i+1))个数可以任意修改,即方案数为:

    [C_{n-(t2-i+1)}^{k-(t2-i+1)} ]

    而对于(t1,t2),我们可以在枚举(i)的同时进行维护。

    特殊情况

    注意,有两种特殊情况会把上面的推理过程卡掉。

    • (a_i=0)
      • 问题:我们会发现,根据(t2)的定义,此时(t2<i),会出锅。
      • 解决方案:每次将(t2)(i)(max)
    • 出现相同的(a_i)
      • 问题:在讨论过程中一些边界细节问题上会出锅。
      • 解决方案:不难发现,相同的(a_i)答案是一样的。而对于一些相同的(a_i),其中第一个(a_i)用上述方法计算答案是不会出错的。因此将所有相同的(a_i)的答案都赋为第一个(a_i)的答案即可。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg 
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100000
    #define X 998244353
    #define C(x,y) ((x)>=(y)?1LL*Fac[x]*IFac[y]%X*IFac[(x)-(y)]%X:0)
    using namespace std;
    int n,k,ans[N+5],Fac[N+5],IFac[N+5];
    struct data {int p,v;I bool operator < (Con data& o) Con {return v<o.v;}}s[N+5];
    I int Qpow(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
    int main()
    {
    	RI i,t1,t2,lst;for(scanf("%d%d",&n,&k),Fac[0]=i=1;i<=n;++i) Fac[i]=1LL*Fac[i-1]*i%X;//预处理阶乘
    	for(IFac[n]=Qpow(Fac[n],X-2),i=n-1;~i;--i) IFac[i]=1LL*IFac[i+1]*(i+1)%X;//预处理阶乘逆元
    	for(i=1;i<=n;++i) scanf("%d",&s[s[i].p=i].v);//读入
    	for(sort(s+1,s+n+1),s[0].v=-1,i=1,t1=t2=0;i<=n;++i)//记得排序
    	{
    		if(s[i].v==s[i-1].v) {ans[s[i].p]=lst;continue;}//如果与上一个数一样
    		W(t1^n&&2*s[t1+1].v<s[i].v) ++t1;W(t2^n&&s[t2+1].v<2*s[i].v) ++t2;t2<i&&(t2=i);//更新t1,t2
    		ans[s[i].p]=lst=(C(n-(i-t1),k)+(t2-i<=k?C(n-(t2-i+1),k-(t2-i+1)):0))%X;//计算答案
    	}
    	for(i=1;i<=n;++i) printf("%d
    ",ans[i]);return 0;//输出答案
    }
    
  • 相关阅读:
    关于linux内核模块Makefile的解析
    The Kernel Newbie Corner: Kernel Debugging with proc "Sequence" Files--Part 3
    The Kernel Newbie Corner: Kernel Debugging with proc "Sequence" Files--Part 2
    The Kernel Newbie Corner: Kernel Debugging Using proc "Sequence" Files--Part 1
    __KERNEL__ macro
    代码片段------find批量处理
    poll机制分析
    initrd映像文档的作用和制作
    为什么文件名要小写?
    CSS媒体查询 width VS device-width
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu5368.html
Copyright © 2020-2023  润新知