[LOJ6077][2017 山东一轮集训 Day7]逆序对

博客围绕逆序对问题展开,题解部分先想到用dp,通过将数字从小到大加入,联想到生成函数。对分母和分子分别分析,分母用隔板法理解,分子用dp处理,给出状态转移方程式,最后合并两部分得到答案,时间复杂度为O(klogk+n),还提及了源码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

逆序对

题解

首先,看到这道题,应该很容易想到 d p dp dp
我们将所有的数字从小到大加入,当加入数字 i i i时,它有 i i i个可选择位置,可能产生 [ 0 , i − 1 ] [0,i-1] [0,i1]个逆序对。
所以容易联想到生成函数,
A n s = ( ∏ i = 1 n ( ∑ j = 0 i − 1 x j ) ) [ x k ] = ( ∏ i = 1 n 1 − x i 1 − x ) [ x k ] = ∏ i = 1 n ( 1 − x i ) ( 1 − x ) n Ans=(\prod_{i=1}^{n}(\sum_{j=0}^{i-1}x^j))[x^k]=(\prod_{i=1}^{n}\frac{1-x^i}{1-x})[x^k]=\frac{\prod_{i=1}^{n}(1-x^i)}{(1-x)^n} Ans=(i=1n(j=0i1xj))[xk]=(i=1n1x1xi)[xk]=(1x)ni=1n(1xi)

对于分母 1 ( 1 − x ) n \frac{1}{(1-x)^n} (1x)n1,它是与 ∑ j = 0 ( j + n − 1 n − 1 ) x j \sum_{j=0}\binom{j+n-1}{n-1}x^j j=0(n1j+n1)xj等价的。
由于, 1 ( 1 − x ) n = ( 1 + x + x 2 + . . . + x n ) n \frac{1}{(1-x)^n}=(1+x+x^2+...+x^n)^n (1x)n1=(1+x+x2+...+xn)n,那么对于 x i x^i xi,它的次数可以被划分成 n n n段,对应每一种乘到它的路径,划分数可以用隔板法进行理解,我们最后有 i i i个空位与 n − 1 n-1 n1个隔板,相当于我们要从最开始的 i + n − 1 i+n-1 i+n1个元素中选出 n − 1 n-1 n1个隔板,系数为 ( j + n − 1 n − 1 ) \binom{j+n-1}{n-1} (n1j+n1)

而对于我们的分子,相当于我们有 n n n个价值分别为 1 , 2 , 3 , . . . , n 1,2,3,...,n 1,2,3,...,n的物品,我们要选出一些物品,使他们的价值总和为 i i i,如果选择的物品数量为奇数,贡献就为 − 1 -1 1,数量为偶数贡献就为 1 1 1,求所有方法的贡献和。
由于所有物品的价值不相同,所以我们选择的物品数量是 n \sqrt{n} n 级别的,我们可以用 d p dp dp来进行处理。
我们令 d p i , j dp_{i,j} dpi,j表示已经选择了 i i i个物品,它们的总和为 j j j d p dp dp的转移我们可以考虑采取那种一层一层消的思路,转移有两类:

  • 让已经选择了的所有的物品的权值加 1 1 1
  • 在上一个操作的基础上再加入一个权值为 1 1 1的物品。

容易得到状态转移方程式,
d p i , j = d p i − 1 , j − i + d p i , j − i − [ j > n ] d p i − 1 , j − n − 1 dp_{i,j}=dp_{i-1,j-i}+dp_{i,j-i}-[j>n]dp_{i-1,j-n-1} dpi,j=dpi1,ji+dpi,ji[j>n]dpi1,jn1
后面减去 d p i − 1 , j − n dp_{i-1,j-n} dpi1,jn是为了排除选择物品的价值超过 n n n的情况,由于我们一次只会加一,所以价值超过 n n n的物体都是从合法状态转移而来的,价值刚好为 n + 1 n+1 n+1,将其减去剩下的都是合法的了。
然后再将两部分合并在一起就可以得到答案了,由于我们求的只是 x k x^k xk这一项的系数,完全没必要通过 N T T NTT NTT来进行合并,直接乘在一起即可,答案即为
A n s = ∑ i = 0 n ( − 1 ) i ∑ j = 0 k ( k − j + n − 1 n − 1 ) d p i , j Ans=\sum_{i=0}^{n}(-1)^i\sum_{j=0}^{k}\binom{k-j+n-1}{n-1}dp_{i,j} Ans=i=0n(1)ij=0k(n1kj+n1)dpi,j

时间复杂度 O ( k l o g   k + n ) O\left(klog\,k+n\right) O(klogk+n)

源码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 200005
#define MAXM 100005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
const int mo=1e9+7;
const int jzm=2333;
const int lim=10000000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-5;
typedef pair<int,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
int n,k,fac[MAXN],inv[MAXN],f[MAXN],dp[2][MAXM],g[MAXM],ans; 
void init(){
	fac[0]=fac[1]=inv[0]=inv[1]=f[1]=1;
	for(int i=2;i<=n+k;i++){
		fac[i]=1ll*i*fac[i-1]%mo;
		f[i]=1ll*(mo-mo/i)*f[mo%i]%mo;
		inv[i]=1ll*f[i]*inv[i-1]%mo;
	}
}
int C(int x,int y){
	if(x<0||y<0||x<y)return 0;
	return 1ll*fac[x]*inv[y]%mo*inv[x-y]%mo;
}
signed main(){
	read(n);read(k);init();dp[0][0]=1;
	for(int i=0;i<=k;i++)g[i]=C(i+n-1,n-1);ans=g[k];
	for(int i=1;i<=n;i++){
		if(1ll*i*(i+1)/2LL>k)break;
		int now=i&1,las=now^1;
		for(int j=i;j<=k;j++){
			dp[now][j]=add(dp[las][j-i],dp[now][j-i],mo);
			if(j>n)dp[now][j]=add(dp[now][j],mo-dp[las][j-n-1],mo);
			if(i&1)ans=add(ans,mo-1ll*g[k-j]*dp[now][j]%mo,mo);
			else ans=add(ans,1ll*g[k-j]*dp[now][j]%mo,mo);
		}
		for(int j=i-1;j<=k;j++)dp[las][j]=0;
	}
	printf("%d\n",ans);
	return 0;
}

谢谢!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值