題目鏈接
題意:
有兩個管道,管道1有n個球,管道2有m個球
球有A和B兩種顏色
從A或B分別取球,可能有C(n+m,n)種方案
這些方案中可能有取出球序列相同的
假設總共有k種不同的球序列,ai爲每種的方案數
那麼∑i=1kai=C(n+m,n)
求∑i=1kai2 mod 1024523
題解:
n,m<=500
求∑i=1kai可以直接得出
但是要求求的結果是∑i=1kai2
如果這道題(n+m)比較小,可以考慮二進制枚舉
對於每種進行平方求和,但是這裏n,m太大,肯定不夠用
所以我們就可以把問題轉化
一般對於k次方,你就可以把他轉化成k個人玩這個遊戲
就比如這道題,是兩次方,所以轉化成2個人玩這個遊戲
然後求這兩個人序列相同的方案數
第一個拿到這個序列的方案數是ai,第二個人是bi
那麼兩個人同時拿到這個序列的方案數就是ai∗bi
對於這道題中,遊戲相同,初始相同,所以ai==bi
所以現在需要考慮的就是2個人玩這個遊戲序列相同的方案數
這樣就很明顯了,應該是用dp來求
然後開始構建dp的模型
狀態應該就是兩個人各自從兩個管道拿的球序列相同的方案
dp[i][j][k][l]表示第一個人從管道1拿i個,管道2拿j個
第二個人從管道1拿k個,管道2拿l個兩人序列相同的方案
但是四維DP不論時間空間都不允許
考慮一下降維,我們會發現,兩個人序列相同,拿的個數肯定一樣
所以i+j==k+l,那麼就可以去掉l這一維
然後考慮狀態轉移,由於是拿的個數
我們考慮的應該是往下一個狀態轉移
如果第一個人管道1(管道2)的當前球和第二個人管道2的當前球一樣
那麼就可以讓這兩個人分別拿相同球,並得到當前狀態的貢獻
這樣就可以形成狀態轉移(詳細見下代碼)
然後可以看到,這個遞推永遠是往下一個狀態轉移
也就是球數增長的方向,不需要前一步的值
這樣的話,對於最外層循環那一維,就可以使用滾動數組
最後得到dp[n][m][n]即可
AC代碼
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int mod=1024523;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
int n,m;
int dp[2][510][510];
string a,b;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>m;
cin>>a>>b;a='.'+a,b='.'+b;
dp[0][0][0]=1;
int cur=0;
for(int i=0;i<=n;i++,cur^=1)
for(int j=0;j<=m;j++)
for(int k=0;k<=n;k++){
int l=i+j-k;
int &x=dp[cur][j][k];
if(l<0||l>m)continue;
if(a[i+1]==a[k+1])dp[cur^1][j][k+1]=(dp[cur^1][j][k+1]+x)%mod;
if(a[i+1]==b[l+1])dp[cur^1][j][k]=(dp[cur^1][j][k]+x)%mod;
if(b[j+1]==a[k+1])dp[cur][j+1][k+1]=(dp[cur][j+1][k+1]+x)%mod;
if(b[j+1]==b[l+1])dp[cur][j+1][k]=(dp[cur][j+1][k]+x)%mod;
x=0;
}
cout<<dp[cur][m][n];
return 0;
}