[NOI2009]管道取珠

链接P1758 [NOI2009]管道取珠

  • 要肝出这道题,你需要知道这样一个\(trick.\)
  • 如果你要求对于一个什么东西求出现次数的平方,等价于同时进行两次操作,计算过程可以匹配的方案数。
  • 这个\(trick\)可以去某次神仙\(yyb\)的暑假一次\(noip\)模拟赛学一下。
  • 话说\(yyb\)那一次是真的毒瘤。话说神仙\(fdf\)当场爆切。
  • 那么$ ∑A_i^2$,可以看成取两次得到了相同的串的方案数。
  • 这样就很套路了,朴素想法设\(f_{i,j,p,q}\)表示第一次\(a\)串在\(i\)\(b\)串在\(j\)位置,第二次\(a\)串在\(p\)\(b\)串在\(q\)位置。
  • 很显然\(i+j=p+q\),那么后面一维度就省掉了,设\(f_{i,j,k}\)表示总长度为\(i\),第一次\(a\)串在\(j\),第二次\(a\)串在\(k\)的方案数。
  • 随便讨论转移一下即可,初始\(f_{0,0,0}=1\),复杂度\(O(n^3)\)
#include<bits/stdc++.h>
#define R register int
using namespace std;
const int N=601;
const int mod=1024523;
int n,m,f[2][N][N];char A[N],B[N];
void add(R &x,R y){x=(x+y>=mod?x+y-mod:x+y);}
int main(){
    cin>>n>>m,scanf("%s",A+1),scanf("%s",B+1);
    reverse(A+1,A+n+1),reverse(B+1,B+m+1);
    R now=1,pre=0;f[now][0][0]=1;
    for(R len=1;len<=n+m;++len){
        now^=1,pre^=1;
        for(R i=max(0,len-m);i<=min(n,len);++i)
            for(R j=max(0,len-m);j<=min(n,len);++j)
                f[now][i][j]=0;
        for(R i=max(0,len-m);i<=min(n,len);++i){
            for(R j=max(0,len-m);j<=min(n,len);++j){
                R p=len-i,q=len-j;
                if(i&&j&&A[i]==A[j])add(f[now][i][j],f[pre][i-1][j-1]);
                if(i&&q&&A[i]==B[q])add(f[now][i][j],f[pre][i-1][j]);
                if(p&&j&&B[p]==A[j])add(f[now][i][j],f[pre][i][j-1]);
                if(p&&q&&B[p]==B[q])add(f[now][i][j],f[pre][i][j]);
            }
        }
    }
    cout<<f[now][n][n]<<endl;
    return 0;
}

posted @ 2018-11-03 20:46  Tyher  阅读(141)  评论(0编辑  收藏  举报