『STA - R9』咏叹调调律 题解
不愧是年度试题推荐选集的题目,再次让我感受到计数题的恐怖。
首先最关键也是最难想到的一步就是,转化为括号序列。观察可以消掉的结构,CA,CCB,AB,AAA,启示我们 C 可以看作 '(',而 B 可以看作 '))',A 最复杂,是 '((' 或者 ')'。
然后还有一个观察就是,如果一个作为左双括号的 A 在一个作为右括号的 A 右边的话,交换这两个的表示内容是不劣的,因为我们是在对子序列做匹配。所以有一个分界线,在线左边都是左 A,在线右边都是右 A。
然而即使变成了括号序列也有问题,因为一个字符串可能有多种匹配方式,所以直接算会算重。而且,即使括号序列是合法的,这个串也不一定是合法的,如 AACB。
怎么解决呢?我们引入一个优先级规则,即双括号优先匹配,B 会优先匹配 A 而非 CC。然后为了解决后面那个合法匹配问题,我们发现,一个左 A 如果已经匹配了一个右 A,还剩的那个括号就不能给别人匹配,只能再用一个右 A。当然如果有 B 来匹配这半个左 A 的话,就要脱离当前匹配的右 A,并且在保证有一个 C 来和这个右 A 匹配的情况下进行。
总上所述,我们设计了一个DP,令 \(f_{i,j,k,0/1,0/1/2}\) 表示填前 \(i\) 个数,还有 \(j\) 个左 A,\(k\) 个 C 等待匹配,目前只能填左 A/右 A,目前没有特殊左 A/有特殊左 A 且有 C 储备/有特殊左 A 且无 C 储备时的答案。
考虑转移:
1.加入左 A,则 \(l=0\),下一状态的 \(j\) 加一。
2.加入 C,无条件,\(k\) 加一。
3.加入右 A,则 \(l=1\),分成几种情况匹配,即匹配掉特殊左 A,匹配掉 C,创造一个特殊左 A。注意这里,创造特殊左 A 时如果有 C,则 \(k\) 减一,因为这个 C 被预留给后面,不参与其他匹配,同理当匹配掉一个特殊左 A 时,如果有 C 就要释放出来,令 \(k\) 加一。
4.加入 B,也分几种情况,按优先级排序是,匹配掉左 A,匹配掉含 C 储备的特殊左 A,匹配掉 CC。
答案为 \(f_{i,0,0,1,0}+f_{i,0,0,0,0}\),时间复杂度 \(O(n^3)\)。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define _int __int128
#define ull unsigned long long
#define pii pair<int,int>
#define fst first
#define scd second
#define pq priority_queue
#define mkp make_pair
#define popcount(x) __builtin_popcount(x)
int n,p,q,r;
const int N = 505,mod=998244353;
int f[2][N][N][2][3];
inline void add(int &x,int y){
x=(x+y>=mod)?x+y-mod:x+y;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>p>>q>>r;
f[0][0][0][0][0]=1;
for(int i=0;i<n;i++){
for(int j=0;j<=i;j++){
for(int k=0;j+k<=i;k++){
for(int l=0;l<2;l++){//只能放左A/右A
for(int s=0;s<3;s++){//无特殊左A/有特殊左A,有C/有特殊左A,无C
int fv=f[i&1][j][k][l][s];
if(!fv)continue;
//加左A
if(!l)add(f[(i+1)&1][j+1][k][l][s],fv*p%mod);
//加C
add(f[(i+1)&1][j][k+1][l][s],fv*r%mod);
//加右A
if(s==2)add(f[(i+1)&1][j][k][1][0],fv*p%mod);
else if(s==1)add(f[(i+1)&1][j][k+1][1][0],fv*p%mod);
else if(j&&k)add(f[(i+1)&1][j-1][k-1][1][1],fv*p%mod);//创造一个有 C 的特殊左 A
else if(j)add(f[(i+1)&1][j-1][k][1][2],fv*p%mod);;//创造一个没 C 的特殊左 A
else if(k)add(f[(i+1)&1][j][k-1][1][s],fv*p%mod);
//加B
if(j)add(f[(i+1)&1][j-1][k][l][s],fv*q%mod);
else if(s==1)add(f[(i+1)&1][j][k][1][0],fv*q%mod);
else if(k>1)add(f[(i+1)&1][j][k-2][l][s],fv*q%mod);
f[i&1][j][k][l][s]=0;
}
}
}
}
cout<<(f[(i+1)&1][0][0][0][0]+f[(i+1)&1][0][0][1][0])%mod<<" ";
}
return 0;
}

浙公网安备 33010602011771号