1 条题解

  • 0
    @ 2026-5-7 22:12:42

    注意到统计“长度为 ii,逆序对数为 jj”的排列数就需要 Θ(n3)\Theta(n^3) 背包处理,所以我们需要一个足够优秀的做法来刻画“在固定点处的深度”。

    发现题目中给出的就是笛卡尔树。考虑用一下它的性质。首先,笛卡尔树的值是堆性质,跟逆序数无关,所以我们不能往这方面考虑。那么我们就只能放着这个丑陋的逆序数,那么还能优化的点一定在深度上。

    【引理】 笛卡尔树上的一个下标为 ii 的点,其祖先一定是下面的两类点之一:

    1. 1i1\sim i 中的后缀最小值。
    2. ini\sim n 中的前缀最小值。

    注意这里 ii 自己在两种情况中都满足。

    证明考虑探索 ii 在树上的父亲应该是谁(实际上,应该是 arg min(aLi,aRi)\argmin(a_{L_i},a_{R_i})),然后归纳即得。

    好的,那么这个有什么用?考虑 dp 刻画这个前后缀的最小值?显然不行,你多记录一维就寄了。怎么办才能不增加“硬复杂度(状态定义)”而刻画前后缀呢?考虑拆贡献!

    相当于就是要求你一个 qq 是另一个点 pp 的祖先时的方案数。这可以分三类讨论:

    1. p<qp<q。这个情况的讨论是下面的重点。
    2. p=qp=q。显然这个的贡献为满足逆序数为 kk 的排列的总个数。开头加一下就行。
    3. p>qp>q。这和 p<qp<q 没有本质区别。

    下面我们考虑 p<qp<q 的情况。我们考虑以一定的顺序从值域中插入每个数。先加入 pqp\sim q 之间的值,然后再加 1p1,q+1n1\sim p-1,q+1\sim n 两边的值。发现这样加入时,你 qq 的放置位置是确定的(就是插入在此时的最小值),而其他的和原来一样都是爱放哪里放哪里,因此我们可以做到 O(n5)\mathcal O(n^5) 了。注意放 qq 时,显然会对逆序对造成 qpq-p 的贡献需要加上,算答案时要小心。(而 p>qp>q 时则不必,因为贡献的是顺序对)

    考虑怎么做到 Θ(n4)\Theta(n^4)。注意到你的加背包加的是一个“权值为 11,个数为给定值的多重背包”,而这可以以前缀和搞定。那么我们发现这样的权值为 11 的多重背包是可以退背包的,只需要把前缀和变成差分即可。这可以很容易的实现,可以参考代码。当然,这个过程也可以用生成函数多项式除法刻画,一切背包都是多项式。这样子你要退平方次背包,每次需要逆序对数量也就是平方的复杂度,一共就是四次方了。

    那么如何优化到 Θ(n3)\Theta(n^3) 呢?大眼观察,发现你实际上退的背包和带来的各种常数都只和 qp|q-p| 有关!那么你只需要退 nn 次背包,同时记录各种权值。那么这个题就做完了。

    Code Below.

    #include <bits/stdc++.h>
    #define rep(i, a, b) for (int i = (a), i##ABRACADABRA = (b); i <= i##ABRACADABRA; i++)
    #define drep(i, a, b) for (int i = (a), i##ABRACADABRA = (b); i >= i##ABRACADABRA; i--)
    using namespace std;
    using ll = long long;
    
    ll mod,ans[505],val1[505],val2[505];
    int n,K;
    
    struct Knapsack{
      ll f[250010],s[250010];
      Knapsack(){
        memset(f,0,sizeof(f));
        memset(s,0,sizeof(s));
        f[0]=1;
        rep(i,0,250005)s[i]=1;
      }
      void add(int x){
        rep(i,0,K+1){
          f[i]=s[i];
          if (i-x-1>=0)(f[i]+=mod-s[i-x-1])%=mod;
        }
        s[0]=f[0];
        rep(i,1,K+1)s[i]=(s[i-1]+f[i])%mod;
      }
      void del(int x){
        rep(i,0,K+1){
          s[i]=f[i];
          if (i-x-1>=0)(s[i]+=s[i-x-1])%=mod;
        }
        f[0]=s[0];
        rep(i,1,K+1)f[i]=(s[i]-s[i-1]+mod)%mod;
      }
    }ds;
    
    int main() {
      scanf("%d%d%lld",&n,&K,&mod);
      rep(i,1,n-1)ds.add(i);
      rep(i,1,n)ans[i]=ds.f[K];
      rep(dif,1,n-1){
        ds.del(dif); // O(n^3)
        if (K-dif>=0)val1[dif]=ds.f[K-dif];
        val2[dif]=ds.f[K];
        ds.add(dif);
      }
      rep(p,1,n)rep(q,p+1,n){
        (ans[p]+=val1[q-p])%=mod;
        // ds.del(q-p); // O(n^4)
        // if (K-q+p>=0)(ans[p]+=ds.f[K-q+p])%=mod;
        // ds.add(q-p);
      }
      rep(p,1,n)rep(q,1,p-1){
        (ans[p]+=val2[p-q])%=mod;
        // ds.del(p-q);
        // (ans[p]+=ds.f[K])%=mod;
        // ds.add(p-q);
      }
      rep(i,1,n)printf("%lld%c",ans[i]," \n"[i==n]);
      
      return 0;
    }
    
    • 1

    信息

    ID
    6938
    时间
    2000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    2
    已通过
    2
    上传者