权威的手机排行榜网站cpa广告联盟平台
P8273 [USACO22OPEN] Pair Programming G
题目大意
一个程序由一系列指令组成,每条指令的类型如下:
- × d \times d ×d,其中 d d d是一个 [ 0 , 9 ] [0,9] [0,9]范围内的整数
- + s +s +s,其中 s s s是一个表示变量名称的字符串,不同操作的变量名称互不相同
指令字符串中一个数字 d d d表示操作一,一个加号 + + +表示操作二。
小 B B B和小 E E E各有一个有 n n n条指令的程序,交错这些程序可以得到一个有 2 n 2n 2n个指令的新程序。计算执行这些交错程序可能得到的不同表达式的数量,输出答案对 1 0 9 + 7 10^9+7 109+7取模后的值。
有 T T T组数据。
1 ≤ T ≤ 10 , ∑ n ≤ 2000 1\leq T\leq 10,\sum n\leq 2000 1≤T≤10,∑n≤2000
题解
首先,要对小 B B B和小 E E E的字符串做一些处理。如果有 × 0 \times 0 ×0的操作,则之前的部分都没用了,但注意这个 × 0 \times 0 ×0还有用。如果有 × 1 \times 1 ×1操作,则对答案没有影响,要将其舍去。
设 f i , j , 0 / 1 f_{i,j,0/1} fi,j,0/1表示小 B B B的串匹配到第 i i i位,小 E E E的串匹配到第 j j j位,最后一个是小 B B B的指令还是小 E E E的指令的答案。
我们知道,加法和乘法都满足交换律,即 a + b = b + a , a × b = b × a a+b=b+a,a\times b=b\times a a+b=b+a,a×b=b×a。也就是说,如果有两个类型相同且位置相邻的操作,则两个操作的先后顺序对最后得到的表达式没有影响。
那么,如果两个字符串当前的操作类型相同,则先执行小 B B B的操作。
由此可推得转移式
f i + 1 , j , 0 = f i , j , 0 + f i , j , 1 f_{i+1,j,0}=f_{i,j,0}+f_{i,j,1} fi+1,j,0=fi,j,0+fi,j,1
f i , j + 1 , 1 = f i , j , 0 × [ S i ≠ T j + 1 ] + f i , j , 1 f_{i,j+1,1}=f_{i,j,0}\times [S_i\neq T_{j+1}]+f_{i,j,1} fi,j+1,1=fi,j,0×[Si=Tj+1]+fi,j,1
当最后一个指令是小 B B B的指令时,如果下一个指令要是小 E E E的指令,则必须满足两个指令的类型不同。这样就能保证没有表达式会被重复计算。
注意一开始 f 0 , 0 , 1 = 1 f_{0,0,1}=1 f0,0,1=1,如果将 1 1 1赋值到 f 0 , 0 , 0 f_{0,0,0} f0,0,0则会在下一次转移中被计算两次,所以要将 1 1 1赋值到 f 0 , 0 , 1 f_{0,0,1} f0,0,1才能使开始的空字符串只被计算一次。
时间复杂度为 O ( ∑ n 2 ) O(\sum n^2) O(∑n2)。
code
#include<bits/stdc++.h>
using namespace std;
const long long mod=1e9+7;
int T,n,s1,t1;
long long f[2005][2005][2];
char s[2005],t[2005];
void dd(int &a1,char a[]){a1=0;for(int i=1;i<=n;i++){if(a[i]=='0') a1=0;else if(a[i]=='1') continue;if(a[i]!='+') a[i]='*';a[++a1]=a[i];}
}
int main()
{scanf("%d",&T);while(T--){scanf("%d",&n);scanf("%s%s",s+1,t+1);dd(s1,s);dd(t1,t);f[0][0][1]=1;for(int i=0;i<=s1;i++){for(int j=0;j<=t1;j++){if(i<s1) f[i+1][j][0]=(f[i][j][0]+f[i][j][1])%mod;if(j<t1){f[i][j+1][1]=f[i][j][1];if(i&&s[i]!=t[j+1])f[i][j+1][1]=(f[i][j+1][1]+f[i][j][0])%mod;}}}printf("%lld\n",(f[s1][t1][0]+f[s1][t1][1])%mod);}return 0;
}