洛谷P1758 [NOI2009]管道取珠(dp 贡献转化)

题目

bzoj1566

洛谷P1758 [NOI2009]管道取珠(dp 贡献转化)_第1张图片

两个管道的小球序列,

分别用长为n(n<=500)和长为m(m<=500)的仅由A和B构成的字符串表示

两个管道归并的时候,每次可以从上管道取一个球,也可以从下管道取一个球

假设最终可能产生的不同种类的输出序列共有K种,

其中第i种输出序列的产生方式(即不同的操作方式数目)有ai个。

聪明的小X早已知道,

因此,小X希望计算得到

你能帮助他计算这个值么?

由于这个值可能很大,因此只需要输出该值对1024523的取模即可(即除以1024523的余数)。

说明:文中C(n+m,n)表示组合数。组合数C(a,b)等价于在a个不同的物品中选取b个的选取方案数。

思路来源

https://www.luogu.com.cn/blog/user23116/solution-p1758 第一种dp

https://blog.csdn.net/pocket_lengend/article/details/79821748 第二种dp

题解

首先要理解一点,最终出现的每一个序列如果出现ai次,其贡献是ai^2

这等价于,对一个序列取两次,取两次的过程中出现两个序列相同的方案数,

设某个序列出现了a次,则在第一次里出现了a次,第二次里也出现了a次,

则对于该序列来说,第一次=第二次的方案数为a*a次,即a^2次,

理解了这一点后,有两种dp方式,

一种是dp[i][j][k][l]表示第一次第一个管道取了i个,第一次第二个管道j个

第二次第一个管道取了k个,第二次第二个管道取了l个的完全相同序列(p,q)对的方案数

把第四维优化掉就是dp[i][j][k],再把第一维滚动一下得到dp[2][505][505]

另一种是考虑总长,i+j=k+l,

所以dp[len][j][k]表示总长为len,第一次第一个管道取了j个,第二次第一个管道取了k个

的完全相同序列(p,q)对的方案数,第二种更便于写转移和理解,虽然看似多了一个二倍的常数

代码

#include
using namespace std;
const int N=505,mod=1024523;
int n,m,dp[2][N][N];
char s[N],t[N];
void add(int &x,int y){
    x+=y;
    x%=mod;
}
int main(){
    dp[0][0][0]=1;
    scanf("%d%d",&n,&m);
    scanf("%s%s",s+1,t+1);
    for(int i=0,f=0;i

你可能感兴趣的:(#,dp,贡献,思维题)