Luogu P4463 [国家集训队] calc

本文探讨了一道数学竞赛题目,利用动态规划与拉格朗日插值解决组合价值求和问题。通过分析严格上升序列价值与排列方案数的关系,将问题转化为多项式求值,最终实现高效算法。

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

题目大意

%  给定 [1,A][1,A][1,A] 之间的数,从其中取出 mmm 个数排列,每种方案的价值为序列中所有元素的乘积,求所有方案的价值和在 modmodmod 意义下的值。
   数据范围 1⩽A⩽109,n⩽500,mod∈prime,mod⩽109,mod>A>n+11\leqslant A\leqslant 10^9,n\leqslant 500,mod\in \text{prime},mod\leqslant 10^9,mod>A>n+11A109,n500,modprime,mod109,mod>A>n+1

题解

%  先考虑动态规划,直接问什么定义什么似乎很难转移。那就只能分析题目了。首先,我们发现方案的价值和顺序无关,因而我们可以求出严格上升序列的价值,然后乘上排列方案数 n!n!n!
  定义:用 1∼j1\sim j1j 填满序列的前 iii 个位置,能得到的严格上升序列的总价值为 f[i][j]f[i][j]f[i][j]
  那么转移就很好写了:f[i][j]=f[i−1][j−1]×j+f[i][j−1]f[i][j]=f[i-1][j-1]\times j+f[i][j-1]f[i][j]=f[i1][j1]×j+f[i][j1]  则答案为 f[n][A]×n!f[n][A]\times n!f[n][A]×n!,时间复杂度为 Θ(n×A)\Theta(n\times A)Θ(n×A),对于本题来说显然是错的。
  考虑优化,如果我们知道 f[n][A]f[n][A]f[n][A] 的次数,那就可以套拉格朗日插值了。
  尝试令 f[i][j]f[i][j]f[i][j] 是一个关于 iiig(n)g(n)g(n) 次多项式。我们知道,一个 iii 次多项式差分后为一个 i−1i-1i1 次多项式,因而有:f[i][j]−f[i−1][j]f[i][j]-f[i-1][j]f[i][j]f[i1][j] 为一个 g(i)−1g(i)-1g(i)1 次多项式。对方程式移项,得f[i][j]−f[i][j−1]=f[i−1][j−1]×jf[i][j]-f[i][j-1]=f[i-1][j-1]\times jf[i][j]f[i][j1]=f[i1][j1]×j  等式左侧为 g(i)−1g(i)-1g(i)1 次多项式,右侧为 g(n−1)+1g(n-1)+1g(n1)+1 次多项式,因而有:
g(n)−1=g(n−1)+1g(n)-1=g(n-1)+1g(n)1=g(n1)+1  化简,得:g(n)=g(n−1)+2g(n)=g(n-1)+2g(n)=g(n1)+2  可以用断言并用生成函数证明:g(n)=2ng(n)=2ng(n)=2n  因而原方程为一个 2n2n2n 次的多项式。我们先计算出 f(n,1)∼f[n][2n+1]f(n,1)\sim f[n][2n+1]f(n,1)f[n][2n+1],时间复杂度为 Θ(n)\Theta(n)Θ(n)。然后用拉格朗日插值求出 f[n][A]f[n][A]f[n][A],总时间复杂度为 T(n)=Θ(n2)T(n)=\Theta(n^2)T(n)=Θ(n2)

代码

#include<bits/stdc++.h>
using namespace std;
#define maxn 510
long long A,n;
long long mod;
long long f[maxn][maxn*2];
long long x[maxn],y[maxn];
long long pow_t(long long a,long long b){
	if(b==1) return a%mod;
	long long t=pow_t(a,b>>1);
	if((b&1)==0) return t*t%mod;
	return t*t%mod*a%mod;
}
#define inv(x) pow_t(x,mod-2)
long long ft(long long n,long long k){
	long long ans=0;
	for(int i=0;i<n;i++){
		long long up=y[i]%mod,dn=1;
		for(int j=0;j<n;j++)
			if(i!=j)
				up=up*(k-x[j]+mod)%mod,dn=dn*(x[i]-x[j]+mod)%mod;
		ans=(ans+up*inv(dn)%mod)%mod;
	} return ans;
}
int main(void)
{
	scanf("%lld%lld%lld",&A,&n,&mod);
	long long fac_n=1;
	for(int i=1;i<=n;i++)
		fac_n=fac_n*i%mod;
	for(int i=0;i<=2*n+1;i++)
		f[0][i]=1;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=2*n+1;j++)
			f[i][j]=(f[i-1][j-1]*j%mod+f[i][j-1])%mod;
	for(int i=1;i<=2*n+1;i++)
		x[i-1]=i,y[i-1]=f[n][i];
	printf("%lld",fac_n*ft(2*n+1,A)%mod);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值