1 条题解
-
0
注意到统计“长度为 ,逆序对数为 ”的排列数就需要 背包处理,所以我们需要一个足够优秀的做法来刻画“在固定点处的深度”。
发现题目中给出的就是笛卡尔树。考虑用一下它的性质。首先,笛卡尔树的值是堆性质,跟逆序数无关,所以我们不能往这方面考虑。那么我们就只能放着这个丑陋的逆序数,那么还能优化的点一定在深度上。
【引理】 笛卡尔树上的一个下标为 的点,其祖先一定是下面的两类点之一:
- 中的后缀最小值。
- 中的前缀最小值。
注意这里 自己在两种情况中都满足。
证明考虑探索 在树上的父亲应该是谁(实际上,应该是 ),然后归纳即得。
好的,那么这个有什么用?考虑 dp 刻画这个前后缀的最小值?显然不行,你多记录一维就寄了。怎么办才能不增加“硬复杂度(状态定义)”而刻画前后缀呢?考虑拆贡献!
相当于就是要求你一个 是另一个点 的祖先时的方案数。这可以分三类讨论:
- 。这个情况的讨论是下面的重点。
- 。显然这个的贡献为满足逆序数为 的排列的总个数。开头加一下就行。
- 。这和 没有本质区别。
下面我们考虑 的情况。我们考虑以一定的顺序从值域中插入每个数。先加入 之间的值,然后再加 两边的值。发现这样加入时,你 的放置位置是确定的(就是插入在此时的最小值),而其他的和原来一样都是爱放哪里放哪里,因此我们可以做到 了。注意放 时,显然会对逆序对造成 的贡献需要加上,算答案时要小心。(而 时则不必,因为贡献的是顺序对)
考虑怎么做到 。注意到你的加背包加的是一个“权值为 ,个数为给定值的多重背包”,而这可以以前缀和搞定。那么我们发现这样的权值为 的多重背包是可以退背包的,只需要把前缀和变成差分即可。这可以很容易的实现,可以参考代码。当然,这个过程也可以用生成函数多项式除法刻画,一切背包都是多项式。这样子你要退平方次背包,每次需要逆序对数量也就是平方的复杂度,一共就是四次方了。
那么如何优化到 呢?大眼观察,发现你实际上退的背包和带来的各种常数都只和 有关!那么你只需要退 次背包,同时记录各种权值。那么这个题就做完了。
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
- 上传者