zoukankan      html  css  js  c++  java
  • 【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
    ",fac[n+truem]*power(ans,mod-2)%mod);
        }
    
        return 0;
    }
  • 相关阅读:
    nextSibling VS nextElementSibling
    线程实现连续启动停,并在某一时间段内运行
    线程:安全终止与重启
    监控知识体系
    后台服务变慢解决方案
    Java泛型类型擦除以及类型擦除带来的问题
    常见的 CSRF、XSS、sql注入、DDOS流量攻击
    Spring对象类型——单例和多例
    一次线上OOM过程的排查
    深入浅出理解基于 Kafka 和 ZooKeeper 的分布式消息队列
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793357.html
Copyright © 2011-2022 走看看