2021牛客暑期多校训练营5 D.Double Strings(容斥dp)
Posted issue是fw
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021牛客暑期多校训练营5 D.Double Strings(容斥dp)相关的知识,希望对你有一定的参考价值。
考虑合法的序列,必定有一个分割点 k k k使得 a k < b k a_k<b_k ak<bk
且在此之前相当于可以有一段串 A A A和串 B B B的公共子序列接在前面
在此之后怎么选都无所谓,只需要满足选出来的长度相等即可
我们定义 f [ i ] [ j ] f[i][j] f[i][j]表示 A [ 1 , i ] A[1,i] A[1,i]和 B [ 1 , j ] B[1,j] B[1,j]有多少种公共子序列
当 a i ! = b j a_i!=b_j ai!=bj时,考虑容斥一下
f [ i ] [ j ] = f [ i ] [ j − 1 ] + f [ i − 1 ] [ j ] − f [ i − 1 ] [ j − 1 ] f[i][j]=f[i][j-1]+f[i-1][j]-f[i-1][j-1] f[i][j]=f[i][j−1]+f[i−1][j]−f[i−1][j−1]
当 a i = = b j a_i==b_j ai==bj,此时还可以用这两个字母加在 f [ i − 1 ] [ j − 1 ] f[i-1][j-1] f[i−1][j−1]的后面,所以方案多了 f [ i − 1 ] [ j − 1 ] + 1 f[i-1][j-1]+1 f[i−1][j−1]+1
f [ i ] [ j ] = f [ i ] [ j − 1 ] + f [ i − 1 ] [ j ] + 1 f[i][j]=f[i][j-1]+f[i-1][j]+1 f[i][j]=f[i][j−1]+f[i−1][j]+1
定义 z [ i ] [ j ] z[i][j] z[i][j]表示 A A A串后缀长为 i i i且 B B B串后缀长为 j j j,从中取出子序列满足长度相等的情况
直接抽象成后缀所有字母都相等,求一个公共子序列即可,转移为
z [ i ] [ j ] = z [ i ] [ j − 1 ] + z [ i − 1 ] [ j ] + 1 z[i][j]=z[i][j-1]+z[i-1][j]+1 z[i][j]=z[i][j−1]+z[i−1][j]+1
于是我们直接枚举 a i < b j a_i<b_j ai<bj计算答案,以这个为分割线的方案有
( f [ i − 1 ] [ j − 1 ] + 1 ) ∗ ( z [ n − i ] [ m − j ] + 1 ) (f[i-1][j-1]+1)*(z[n-i][m-j]+1) (f[i−1][j−1]+1)∗(z[n−i][m−j]+1)
其中加 1 1 1是因为前后都可以为空,时间复杂度 O ( n 2 ) O(n^2) O(n2)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5009;
const int mod = 1e9+7;
char a[maxn],b[maxn];
int n,m,f[maxn][maxn],z[maxn][maxn];
signed main()
{
cin >> ( a+1 ) >> ( b+1 );
n = strlen( a+1 ); m = strlen( b+1 );
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
z[i][j] = ( 1ll*z[i-1][j]+z[i][j-1]+1 )%mod;
if( a[i]==b[j] )
f[i][j] = ( 1ll*f[i][j-1]+f[i-1][j]+1 )%mod;
else
f[i][j] = ( 1ll*f[i][j-1]+f[i-1][j]-f[i-1][j-1] )%mod;
}
long long ans = 0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if( a[i]<b[j] )
ans = ( 1ll*ans+( 1+1ll*f[i-1][j-1] )*( 1+z[n-i][m-j] )%mod )%mod;
}
cout << ( ans%mod+mod )%mod;
}
以上是关于2021牛客暑期多校训练营5 D.Double Strings(容斥dp)的主要内容,如果未能解决你的问题,请参考以下文章
2021牛客暑期多校训练营5 C. Cheating and Stealing(模拟,思维)