[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;
}

原文地址:https://www.cnblogs.com/Tyher/p/9902039.html