题目大意
给定N,M,问有多少不同的序列对(A,B)满足:
1. 长度均为N
2. 从A中删去0到2个元素,在B中删去相同数量的数,然后两个序列相同
3. 序列都是[1,M]内的整数
答案对109+9取模,N≤100,1≤M≤1000000000
分析
第2个条件相当于两个序列的最长公共子序列长度不小于n-2
首先考虑对于两个序列,如何求它们的最长公共子序列。
有一个很清晰的n2的方法:设f[i][j]表示A的前i个,B的前j个,求出最长公共子序列的长度。容易得到:
f[i][j]={f[i−1][j−1]+1min(f[i][j−1],f[i−1][j])A[i]=B[j]A[i]≠B[j]
现在用这个DP的思路来构造序列。
设F[i][S]表示确定了两个序列的前i位,S表示最后三位(i-2,i-1,i)数的状态以及f[][]数组最后三位的状态。然后枚举第i+1位的两个数,然后更新f数组,最后存入新状态。具体细节就不说了。
最后三位的状态用最小表示法,最终搜出来状态数很少,可以通过此题。
注意把n=1、2的情况特判掉
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;
const int mo=1e9+9,N=1005;
typedef long long LL;
int n,B,m,p,q,f[2][N];
LL st[2][N],H;
int a[2][4],b[20],c[20],dp[4][4],id[4][4],tot[2];
map <LL,int> h[2];
int v[7];
void init(int x,int y,int s)
{
if (x>6)
{
if (y>m || y>5) return;
for (int i=1;i<4;i++)
{
a[0][i]=b[i*2-1]; a[1][i]=b[i*2];
}
dp[0][0]=0;
for (int i=1;i<=3;i++)
{
for (int j=1;j<=3;j++)
{
if (a[0][i]==a[1][j]) dp[i][j]=dp[i-1][j-1]+1;
else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
b[id[i][j]]=3-dp[i][j];
}
}
if (dp[3][3]==0) return;
H=0;
for (int i=3;i<=15;i++) H=H*6+b[i];
if (h[0][H]==0)
{
h[0][H]=++tot[0];
st[0][tot[0]]=H; f[0][tot[0]]=0;
}
f[0][h[0][H]]=(f[0][h[0][H]]+s)%mo;
return;
}
for (b[x]=1;b[x]<=y;b[x]++) init(x+1,y,s);
init(x+1,b[x],(LL)s*(m-y)%mo);
}
void extend(int x,int y,int s,int i)
{
if (x>6)
{
if (y>5 || y>m) return;
a[0][3]=b[5]; a[1][3]=b[6];
if (a[0][1]==a[1][3]) dp[1][3]=dp[0][2]+1;else dp[1][3]=max(dp[0][3],dp[1][2]);
if (a[0][2]==a[1][3]) dp[2][3]=dp[1][2]+1;else dp[2][3]=max(dp[1][3],dp[2][2]);
if (a[0][3]==a[1][1]) dp[3][1]=dp[2][0]+1;else dp[3][1]=max(dp[3][0],dp[2][1]);
if (a[0][3]==a[1][2]) dp[3][2]=dp[2][1]+1;else dp[3][2]=max(dp[3][1],dp[2][2]);
if (a[0][3]==a[1][3]) dp[3][3]=dp[2][2]+1;else dp[3][3]=max(dp[2][3],dp[3][2]);
if (dp[3][3]<i-2) return;
b[id[1][3]]=i-dp[1][3]; b[id[2][3]]=i-dp[2][3]; b[id[3][1]]=i-dp[3][1]; b[id[3][2]]=i-dp[3][2]; b[id[3][3]]=i-dp[3][3];
H=0;
for (int i=3;i<=15;i++) H=H*6+b[i];
if (h[q][H]==0)
{
h[q][H]=++tot[q];
st[q][tot[q]]=H; f[q][tot[q]]=0;
}
f[q][h[q][H]]=(f[q][h[q][H]]+s)%mo;
return;
}
for (b[x]=1;b[x]<=y;b[x]++) extend(x+1,y,s,i);
extend(x+1,b[x],(LL)s*(m-y)%mo,i);
}
int main()
{
scanf("%d%d",&n,&m);
if (n==1)
{
printf("%d\n",(LL)m*m%mo); return 0;
}
if (n==2)
{
printf("%d\n",(LL)m*m%mo*m%mo*m%mo); return 0;
}
id[1][1]=7; id[1][2]=8; id[1][3]=9;
id[2][1]=10; id[2][2]=11; id[2][3]=12;
id[3][1]=13; id[3][2]=14; id[3][3]=15;
init(1,0,1);
p=0; q=1;
for (int i=4;i<=n;i++,p^=1,q^=1)
{
tot[q]=0;
h[q].clear();
for (int j=1;j<=tot[p];j++)
{
H=st[p][j];
int s=f[p][j];
for (int k=15;k>2;k--,H/=6) c[k]=H%6;
int cnt=0;
memset(v,0,sizeof(v));
for (int k=1;k<5;k++)
if (v[c[k+2]]>0) a[(k&1)^1][(k+1)>>1]=b[k]=v[c[k+2]];else a[(k&1)^1][(k+1)>>1]=b[k]=v[c[k+2]]=++cnt;
for (int p=1;p<3;p++) for (int q=1;q<3;q++)
{
dp[p][q]=i-1-c[id[p+1][q+1]]; b[id[p][q]]=i-dp[p][q];
}
dp[0][0]=i-1-c[id[1][1]]; dp[0][1]=i-1-c[id[1][2]]; dp[0][3]=dp[0][2]=i-1-c[id[1][3]]; dp[1][0]=i-1-c[id[2][1]]; dp[3][0]=dp[2][0]=i-1-c[id[3][1]];
extend(5,cnt,s,i);
}
}
int ans=0;
for (int i=1;i<=tot[p];i++) ans=(ans+f[p][i])%mo;
printf("%d\n",ans);
return 0;
}