LOJ #6077. 「2017 山东一轮集训 Day7」逆序对
思路
没怎么做过这种通过生成函数来表示转移过程的 DP,比较生疏,参考了很久题解,不过过程很有启发性。
考虑最暴力的 DP,设 \(f_{i,j}\) 表示从小到大正在填第 \(i\) 个数,逆序对个数一共有 \(j\) 的方案数。有转移:
含义是考虑将第 \(i\) 个数放在之前的那个位置上,新产生的逆序对个数是 \([0,i-1]\) 的一个连续段,可以从中的所有状态转移过来。
显然 T 飞。发现这个转移的过程很规整,考虑用生成函数来表示转移的过程。对于不存在的 \(f\),我们将其值视为 \(0\)。
设 \(F_i(x)=\sum_{j=0}^{\infty}f_{i,j}x^j\),也就是一般生成函数。那显然转移为
发现确实比较规整,直接一直卷到 \(n\) 就直接有
答案就是其 \(k\) 项系数。考虑和式根本做不了,于是转封闭形式
于是原式可以拆成两部分
左半边的东西显然是好求的,广义二项式定理展开有
后面看起来不是很好直接做。一个比较牛的转化是发现连乘实际上等价于每次选 \(1\) 或者 \(-x^i\),于是问题就转化成了对于一个 \([1,n]\) 的排列,从中选出一些数来,然后和恰好为次数的方案数,这个方案数就是对应次数的系数。
于是我们有一个额外的 DP 去算上面的方案数。设 \(g_{i,j}\) 表示在排列中选了 \(i\) 个数,这些数的和恰好为 \(j\) 的方案数。根据经典套路,我们将其看作全局加 1 或者向里面加数的形式。
具体的,由于我们要求了所有数互不相同,于是我们等价于有两种操作:
- 全局加任意次 1。
- 全局加一次 1 并且向数列中加入一个新的 1。
注意到这一定可以不重不漏地遍历每一种方案。但是这道题特殊在于要求选的数的大小不大于 \(n\)。由于我们每次只可能全局加 1,因此只可能出现 \(n+1\),因此我们只需要将出现了 \(n+1\) 的情况减掉即可。也就是钦定当前数列中有一个 \(n+1\),然后其他数正常选的方案数,发现实际上与 \(g_{i-1,j-n-1}\) 等价。
具体的转移即 \(g_{i,j}\gets g_{i,j-i}+g_{i-1,j-i}-[j>n]g_{i-1,j-n-1}\)。
但是由于我们每次选的是 \(-x^i\),是带符号的,因此我们要判一下符号。写出来即为
这个上下界还是比较好理解的。就是根据其实际意义发现其不可能超过这个上界的值。
于是答案就是两个东西卷起来。
时空复杂度都是 \(O(k\sqrt k)\)。
code
没什么细节。实现的时候由于最原始的 DP 已经隐去了,于是将 \(g\) 写作了 \(f\)。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+7,M=455,p=1e9+7;
int n,K,pw[N<<1],ni[N<<1],f[M][N];
int ksm(int x,int k){
int res=1;while(k){
if(k&1)res=res*x%p;
x=x*x%p;k>>=1;
}
return res;
}
int inv(int x){return ksm(x,p-2);}
int C(int x,int y){if(y>x)return 0;return pw[x]*ni[x-y]%p*ni[y]%p;}
int add(int x,int y){return x+y>=p?x+y-p:(x+y<0?x+y+p:x+y);}
void upd(int &x,int y){x=add(x,y);}
void calc(){
f[0][0]=1;
for(int i=1;i<=450;i++){
for(int j=i;j<=K;j++){
upd(f[i][j],f[i][j-i]+f[i-1][j-i]);
if(j>=n+1)upd(f[i][j],-f[i-1][j-n-1]);
}
}
}
signed main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>K;pw[0]=1;for(int i=1;i<=n+K;i++)pw[i]=pw[i-1]*i%p;
ni[n+K]=inv(pw[n+K]);for(int i=n+K-1;i>=0;i--)ni[i]=ni[i+1]*(i+1)%p;
calc();int ans=0;
for(int i=0;i<=K;i++){
int tmp=0;
for(int j=0;j<=sqrt(2*(K-i));j++){
upd(tmp,((j)&1?-1ll:1ll)*f[j][K-i]);
}
upd(ans,tmp*C(n+i-1,i)%p);
}
cout<<ans;return 0;
}

浙公网安备 33010602011771号