1 条题解

  • 0
    @ 2025-8-24 22:52:18

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar 樱雪喵
    无尽欺骗,无限祈愿 | AFOed | qq: 3480681868

    搬运于2025-08-24 22:52:18,当前版本为作者最后更新于2023-11-11 19:45:33,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    Part 1. k=1k=1

    首先考虑 k=1k=1 的时候怎么做。

    考虑最终的位置 ii 会被多少个区间统计到,不难发现它计入贡献的次数是 bi=i(ni+1)b_i=i(n-i+1)
    也就是说问题转化成,令 ai=f(i)a_i=f(i)bi=i(ni+1)b_i=i(n-i+1),我们要对 aa 序列指定一个顺序,最小化 i=1nai×bi\sum\limits_{i=1}^n a_i\times b_i
    这个问题有显然的贪心,aa 序列升序排序,bb 序列降序排序,对应位相乘即为最小值。证明是容易的:设 a<ba<bc<dc<d,有 (ba)d>(ba)c(b-a)d>(b-a)c,移项即得 ac+bd>ad+bcac+bd>ad+bc

    Part 2. n[29,106]n\in [29,10^6]

    在上述过程中发现这么一个问题,方案本质不同的定义是 pp 不相同,但 f(pi)f(p_i) 中有很多数相同。交换它们中的一部分不会使贡献序列改变,但可以使 pp 改变。因此我们估计,在不改变贡献序列的情况下,本质不同的 pp 是相当多的。

    考虑估计这个具体数目,aa 序列中大约有 n2\frac{n}{2}11n22\frac{n}{2^2}22(暂不讨论上下取整的问题),那么我们至少能搞出 $cnt=\prod\limits_{i=1}^{\log_2 n} (\frac{n}{2^i})! $ 种取到最小值的排列。这里没考虑交换 bb 序列的情况,因此实际值会比这更多。

    这说明 kcntk\le cnt 的时候答案都可以取到最小值。写个代码求上面的式子,可以发现 n=29n=29 的时候 cntcnt 就超过了 101810^{18}。也就是说,n>29n>29kk 为任何值的答案都和 k=1k=1 相等。

    O(n)O(n) 地模拟上述贪心,配合爆搜可以通过前 1313 个点。有 52pts 暴力分,好良心!

    Part 3. n[29,1018]n\in [29,10^{18}]

    这一部分没有单独的部分分,但我们需要加速贪心的过程。
    只要快速求出 aa 序列中每个数的数量,就能找到它们在 bb 序列中对应的连续区间。根据基础位运算知识可以得出值为 ii 的出现次数。
    s(l,r)=i=lrbis(l,r)=\sum\limits_{i=l}^r b_i,推式子:

    $$\begin{aligned} s(l,r)&=\sum_{i=l}^r i(n-i+1)\\ &=(n+1)(\sum_{i=l}^r i)-\sum_{i=l}^r i^2\\ &=(n+1)\frac{(l+r)(r-l+1)}{2} - \frac{r(r+1)(2r+1)}{6}+\frac{l(l-1)(2l-1)}{6} \end{aligned} $$

    现在这两个序列都能快速计算,我们可以在 O(logn)O(\log n) 的时间复杂度内处理单次询问。

    Part 4. n28n\le 28

    看起来即使仅考虑本质不同的 aa 序列,这样的数量依然很多。但是我们发现一条关键性质:这种情况下优美度的值域很小,只有 10410^4 左右。
    考虑基于这个进行 dp。进一步观察,aa 序列中至多有 55 种不同的值。因为优美度已经计入了状态,我们显然只关心它们填了几个,而不关心它们的位置。

    故设 fa,b,c,d,e,sumf_{a,b,c,d,e,sum} 表示 1,2,3,4,51,2,3,4,5 分别填了 a,b,c,d,ea,b,c,d,e 个,总优美度为 sumsum 的方案数。
    转移时枚举当前位填什么数,式子是容易写出的。

    这样的有效状态数在 n=28n=28 时取到上界,大致有 $16\times 9\times 5\times 3\times 2\times 1.1\times 10^4=4.752\times 10^7$ 种状态。开数组时请注意空间消耗。

    结合 Part 3 的做法即可获得 100pts。

    #define int long long
    const int N=1e6+5,mod=998244353;
    int T,n,k;
    int f[16][9][5][3][2][11005];
    int a[N],b[N],cnt[N],mx=11000,now[6],jc[N];
    il int qpow(int n,int k=mod-2)
    {
        int res=1;
        for(;k;n=n*n%mod,k>>=1) if(k&1) res=res*n%mod;
        return res;
    }
    il int S(int x) {x%=mod;return x*(x+1)%mod*(2*x+1)%mod*qpow(6)%mod;}
    il int get(int l,int r,int n)
    {
        if(l>r) return 0;
        if(r>n/2&&l<=n/2) return (get(l,n/2,n)+get(n/2+1,r,n))%mod; 
        if(l>n/2) l=n-l,r=n-r,swap(l,r);
        int res=n%mod*((l+r)%mod)%mod*((r-l+1)%mod)%mod*qpow(2)%mod-(S(r)-S(l-1)+mod)%mod;
        return (res%mod+mod)%mod;
    }
    il void solve(int n)
    {
        int l=1,r=n,res=0;
        for(int i=__lg(n)+1;i;i--)
        {
            int sum=(n>>i)+(n>>(i-1)&1);
            int ls=sum/2,rs=sum-ls;
            if(l<n-r+1) swap(ls,rs);
            (res+=i*get(l,l+ls-1,n+1)%mod)%=mod,(res+=i*get(r-rs+1,r,n+1)%mod)%=mod;
            l+=ls,r-=rs;
        }
        printf("%lld\n",res);
    }
    signed main()
    {
        T=read(); jc[0]=1;
        for(int i=1;i<=15;i++) jc[i]=jc[i-1]*i;
        while(T--)
        {
            n=read(),k=read();
            if(n>28) {solve(n);continue;}
            for(int i=1;i<=n;i++) a[i]=1+__lg(i&(-i));
            for(int i=1;i<=n;i++) b[i]=i*(n-i+1);
            sort(a+1,a+n+1),sort(b+1,b+n+1);
            for(int i=1;i<=n;i++) cnt[i]=0;
            for(int i=1;i<=n;i++) cnt[a[i]]++;
            int Cnt=1;
            if(n<=28) for(int i=1;i<=__lg(n)+1;i++) Cnt=Cnt*jc[cnt[i]];
            f[0][0][0][0][0][0]=1;
            for(now[1]=0;now[1]<=cnt[1];now[1]++)
            for(now[2]=0;now[2]<=cnt[2];now[2]++)
            for(now[3]=0;now[3]<=cnt[3];now[3]++)
            for(now[4]=0;now[4]<=cnt[4];now[4]++)
            for(now[5]=0;now[5]<=cnt[5];now[5]++)
            {
                int sum=0;
                for(int i=1;i<=5;i++) sum+=now[i];
                if(!sum) continue;
                for(int j=0;j<=mx;j++)
                {
                    int i=0;
                    for(int k=1;k<=5;k++)
                    {
                        if(now[k]==0) continue;
                        if(j-k*b[sum]<0) continue;
                        now[k]--;
                        i+=f[now[1]][now[2]][now[3]][now[4]][now[5]][j-k*b[sum]];
                        now[k]++;
                    }
                    f[now[1]][now[2]][now[3]][now[4]][now[5]][j]=i;
                }
            }
            int ans=0;
            for(int i=0;i<=mx;i++)
            {
                ans+=Cnt*f[cnt[1]][cnt[2]][cnt[3]][cnt[4]][cnt[5]][i];
                if(ans>=k) {printf("%lld\n",i);break;}
            }
        }
        return 0;
    }
    
    • 1

    信息

    ID
    9192
    时间
    2000~5000ms
    内存
    512MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者