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

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

#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;
}

猜你喜欢

转载自blog.csdn.net/maxwei_wzj/article/details/80436737