【康复训练】[国家集训队] calc【dp】【拉格朗日插值】

本文介绍了一种使用动态规划(DP)解决特定传送门问题的方法。通过构建递推关系,文章详细阐述了如何求解在有限制条件下通过传送门到达目标的方案数,并通过代码示例展示了具体的实现过程。

传送门

显然可以dp

显然可以假设单调增,答案乘个阶乘即可

f ( i , j ) f(i,j) f(i,j)表示前 i i i个不超过 j j j的答案

f ( i , j ) = f ( i , j − 1 ) + j f ( i − 1 , j − 1 ) f(i,j)=f(i,j-1)+jf(i-1,j-1) f(i,j)=f(i,j1)+jf(i1,j1)

注意边界是 f ( 0 , i ) = 1 ! f(0,i)=1! f(0,i)=1!

注意边界是 f ( 0 , i ) = 1 ! ! f(0,i)=1!! f(0,i)=1!!

注意边界是 f ( 0 , i ) = 1 ! ! ! f(0,i)=1!!! f(0,i)=1!!!

最终答案为 n ! f ( n , A ) n!f(n,A) n!f(n,A)

这样做是 O ( n A ) O(nA) O(nA)的,需要优化

考虑上网搜题解,这样容易得知可以假设 f ( i , j ) f(i,j) f(i,j)是关于 j j j的多项式

人话翻译:把 f f f每一行拆开看成一个个数列,这样每一行有一个多项式通项公式

g ( i ) g(i) g(i)表示 f ( i , j ) f(i,j) f(i,j)的次数

根据转移方程

f ( i , j ) − f ( i , j − 1 ) = j f ( i − 1 , j − 1 ) f(i,j)-f(i,j-1)=jf(i-1,j-1) f(i,j)f(i,j1)=jf(i1,j1)

看着不习惯?

F ( i ) − F ( i − 1 ) = i f ( i − 1 ) F(i)-F(i-1)=if(i-1) F(i)F(i1)=if(i1)

由于左边是多项式,最高项一定消掉了

所以

g ( n ) − 1 = g ( n − 1 ) + 1 g(n)-1=g(n-1)+1 g(n)1=g(n1)+1

g ( n ) = g ( n − 1 ) + 2 g(n)=g(n-1)+2 g(n)=g(n1)+2

显然

g ( 0 ) = 0 g(0)=0 g(0)=0

所以

g ( n ) = 2 n g(n)=2n g(n)=2n

插一下就好了

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
const int N=1005;
using namespace std;
int MOD;
typedef long long ll;
inline int add(const int& a,const int& b){return a+b>=MOD? a+b-MOD:a+b;}
inline int dec(const int& a,const int& b){return a<b? a-b+MOD:a-b;}
inline int qpow(int a,int p)
{
	int ans=1;
	while (p)
	{
		if (p&1) ans=(ll)ans*a%MOD;
		a=(ll)a*a%MOD;p>>=1;
	}
	return ans;
}
int dp[N][N];
int main()
{
	int a,n;
	scanf("%d%d%d",&a,&n,&MOD);
	for (int i=0;i<=(2*n);i++) dp[0][i]=1;
	for (int i=1;i<=n;i++)
		for (int j=1;j<=(2*n);j++)
			dp[i][j]=add(dp[i][j-1],(ll)j*dp[i-1][j-1]%MOD);
	int ans=0;
	for (int i=0;i<=2*n;i++)
	{
		int mul=dp[n][i];
		if (!mul) continue;
		for (int j=0;j<=2*n;j++) if (i!=j) mul=(ll)mul*dec(a,j)%MOD*qpow(dec(i,j),MOD-2)%MOD;
		ans=add(ans,mul);
	}
	for (int i=1;i<=n;i++) ans=(ll)ans*i%MOD;
	printf("%d\n",ans);
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值