XJOI 3877 红蓝字符串
题意
给你一个偶数长度的字符串,你想要给每一个字符标记成蓝色或者红色,使得红色的字符序列等于蓝色的字符序列,一共有多少种方法可以做这件事
输入格式
输入一行包含一个字符串\(S, (2≤|S|≤40)\)
字符串的每个字符为'o'或者'x'
输出格式
输出一个整数
样例输入&输出
样例1
oxox
2
样例2
oooxxx
0
样例3
xoxxox
4
样例4
xo
0
样例5
ooooxoox
8
样例6
ooxxoxox
8
样例7
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
137846528820
分析
本题就是要寻找长度为 \(\frac{|S|}{2}\) 的 \(S\) 的子序列 \(S_1\) 的个数,使 \(S\) 的另一个子序列 \(S_2\) 与 \(S_1\) 相等,且 \(S_2\) 与 \(S_1\) 互斥,即对于任意\(S_1[i],S_2[j],i≠j\)。
我们能确定 \(S_1\) 的长度(即\(\frac{|S|}{2}\)),以及 \(S_1\) 中'o'和'x'的个数,于是想到爆搜穷举每一个可能的 \(S_1\) ,最多搜\(C^{20}_{10}=184756\)次。对于每一个可能的 \(S_1\) ,我们用\(O(n^2)\)的dp去检验它的方案数。设 \(dp[i][j]\) 表示枚举到第 \(i\) 位,其中有 \(j\) 位与 \(S_1\) 匹配的方案数,则
\[if(dp[i-1][j-1]存在且b[j]==a[i])dp[i][j]+=dp[i-1][j-1]
\]
\[if(dp[i-1][j]存在且b[i-j]==a[i])dp[i][j]+=dp[i-1][j]
\]
初始化\(dp[0][0]=1\)
最终答案为\(dp[n][n/2]\)
总时间复杂度为\(O(C^{n}_{n/2}* n^2)≈295609600\),卡卡常能过
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 45
using namespace std;
char s[maxn];
bool a[maxn],b[maxn];
int n,n1,n0;
long long ans,dp[maxn][maxn];
long long check(){
for(int i=0;i<=n;i++){
for(int j=0;j<=(n>>1);j++){
dp[i][j]=0;
}
}
dp[0][0]=1;
for(int i=1;i<=n;i++){
for(int j=0;j<=min(i,(n>>1));j++){
if(j&&dp[i-1][j-1]&&b[j]==a[i])dp[i][j]+=dp[i-1][j-1];
if(j<i&&dp[i-1][j]&&b[i-j]==a[i])dp[i][j]+=dp[i-1][j];
}
}
return dp[n][n>>1];
}
void dfs(int step){
if(step>(n>>1)){
ans+=check();
return;
}
if(n1){
n1--;
b[step]=1;
dfs(step+1);
n1++;
}
if(n0){
n0--;
b[step]=0;
dfs(step+1);
n0++;
}
}
int main(){
scanf("%s",s);
n=strlen(s);
for(int i=1;i<=n;i++){
a[i]=(s[i-1]=='x');
n1+=a[i],n0+=!a[i];
}
n1>>=1,n0>>=1;
dfs(1);
printf("%lld",ans);
return 0;
}