【BZOJ5322】排序游戏(JXOI2018)-贪心

测试地址:排序游戏
做法:本题需要用到贪心。
首先,期望轮数显然就等于所有不同排列的种数,而可重排列的数目是有公式的,假设一共有n个元素,第i种元素出现了di次,那么有:
ans=n!di!
注意到题目中最后生成的是一个包含n+m个元素的排列,那么分子就是固定的了,所以要使ans最大,分母就要越小。又观察到,我们每次加入一个数i,令di为数i在加入前的排列中出现的次数,那么加入后分母就会乘上di+1。所以我们就有一个非常显然的贪心思路:每次选择di最小的i插入即可。
然而插入次数非常大,我们甚至不能用带log的数据结构去维护。考虑这个贪心慢在哪,我们发现它每次插入都把时间花在找最小的di上,导致很多点被重复扫了很多遍。为了优化,我们当然不是选择优化“找最小值”的时间,而是减少整体的插入次数。我们如果把数字大小作为x轴,把出现次数作为y轴,画出一个统计图的形式,我们发现,实际上我们可以一层一层地插入数字,而不是一个一个插入。然而层数还是有可能会很大,注意到一层中能插入的数字个数仅在层数达到某个原来di的时候改变,我们显然可以把这些原先的di从小到大排序,每次我们考虑当前剩余的次数够不够把所有数出现次数都填到当前的di,如果能填到就填,可以通过预处理阶乘,再结合快速幂做到O(logn)的复杂度,那么总的时间复杂度就是O(nlogn)的,我们就解决了这一题。
以下是本人代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll mod=998244353;
int T,n,m;
ll l,r,a[200010],q[200010];
ll last=1,fac[10200020],inv[10200020];

ll power(ll a,ll b)
{
    ll s=1,ss=a%mod;
    while(b)
    {
        if (b&1) s=s*ss%mod;
        ss=ss*ss%mod;b>>=1;
    }
    return s;
}

void calc_fac(ll limit)
{
    if (last>=limit) return;
    for(ll i=last+1;i<=limit;i++)
        fac[i]=fac[i-1]*i%mod;
    inv[limit]=power(fac[limit],mod-2);
    for(ll i=limit;i>=last+1;i--)
        inv[i-1]=inv[i]*i%mod;
    last=limit;
}

int main()
{
    scanf("%d",&T);
    fac[0]=fac[1]=1;
    inv[0]=inv[1]=1;

    while(T--)
    {
        int truem;
        scanf("%d%d%lld%lld",&n,&m,&l,&r);
        truem=m;
        calc_fac(n+m);

        for(int i=1;i<=n;i++)
            scanf("%lld",&a[i]);
        sort(a+1,a+n+1);

        int last=1;
        ll ans=1,tot=0;
        for(int i=2;i<=n;i++)
        {
            if (i>1&&a[i]!=a[i-1])
            {
                if (a[last]>=l&&a[last]<=r)
                    q[++tot]=i-last;
                else ans=ans*fac[i-last]%mod;
                last=i;
            }
        }
        if (a[last]>=l&&a[last]<=r)
            q[++tot]=n-last+1;
        else ans=ans*fac[n-last+1]%mod;
        sort(q+1,q+tot+1);

        int siz=tot;
        tot=r-l+1-tot;
        ll lasth=0;
        last=siz+1;
        for(int i=1;i<=siz;i++)
        {
            ll v=q[i];
            if (m>=(v-lasth)*tot)
            {
                ans=ans*power(inv[lasth]*fac[v],tot)%mod;
                ans=ans*fac[v]%mod;
                m-=(v-lasth)*tot;
                tot++;lasth=v;
            }
            else
            {
                ll A=m/tot,B=m%tot;
                ans=ans*power(inv[lasth]*fac[lasth+A],tot)%mod;
                ans=ans*power(lasth+A+1ll,B)%mod;
                ans=ans*fac[v]%mod;
                m=0;
                last=i+1;
                break;
            }
        }

        if (m)
        {
            ll A=m/tot,B=m%tot;
            ans=ans*power(inv[lasth]*fac[lasth+A],tot)%mod;
            ans=ans*power(lasth+A+1ll,B)%mod;
        }
        else
        {
            while(siz>=last)
            {
                ll v=q[siz--];
                ans=ans*fac[v]%mod;
            }
        }

        printf("%lld\n",fac[n+truem]*power(ans,mod-2)%mod);
    }

    return 0;
}
posted @ 2018-05-24 16:37  Maxwei_wzj  阅读(87)  评论(0编辑  收藏  举报