上课打了一下,菜的真实,直接爆零QAQ。
并不是很难的一场div3,我甚至觉得一个擅长dp的rating1800+选手这场可以AK。
E是一个超简单的最大流问题,可惜当时没看,一直卡D。
D题大体方向没错,如果能冷静分析,就能发现贪心的方法了,可惜并没有。
C是一个均值不等式,我没看出来。
这里补一下F题计数dp,因为F题确实不能自己写出来,立篇博客分析一下。
题意
给定一个由a
、b
、c
、?
符号构成的字符串,你可以用a
、b
、c
替换?
,求由给定的字符串
s
s
s生成的所有无?
字符串中,子序列abc
出现的总共次数。
思路
如果这题没有?
,则是一道很套路的计数dp。
令
d
p
[
i
]
[
0
]
dp[i][0]
dp[i][0]表示由
s
s
s串在
[
1
,
i
]
[1,i]
[1,i]子段内生成串中
a
a
a字符的数量,
d
p
[
i
]
[
1
]
dp[i][1]
dp[i][1]表示由
s
s
s串在
[
1
,
i
]
[1,i]
[1,i]子段内生成串中
a
b
ab
ab子序列的数量,
d
p
[
i
]
[
2
]
dp[i][2]
dp[i][2]表示由
s
s
s串在
[
1
,
i
]
[1,i]
[1,i]子段内生成串中
a
b
c
abc
abc子序列的数量
先考虑不含?
的情况,则当
s
i
=
a
s_i=a
si=a时,
d
p
[
i
]
[
0
]
=
d
p
[
i
−
1
]
[
0
]
+
1
dp[i][0]=dp[i-1][0]+1
dp[i][0]=dp[i−1][0]+1。
则包含?
时,要考虑此时已有多少个生成串,可以发现生成串的数目只和?
的数目有关,记到
i
i
i时?
的数目为
c
i
c_i
ci,则以有的生成串数目为
3
c
i
−
1
3^{c_{i-1}}
3ci−1。
则当 s i = a s_i=a si=a时,对于所有的 3 c i − 1 3^{c_{i-1}} 3ci−1个生成串,一共增加了 3 c i − 1 3^{c_{i-1}} 3ci−1个 a a a。
s
i
s_i
si为
b
b
b和
c
c
c的转移方式是和不包含?
时的转移方式一样的,之前已存在的+新产生的。
d
p
[
i
]
[
1
]
=
d
p
[
i
−
1
]
[
1
]
+
d
p
[
i
−
1
]
[
0
]
d
p
[
i
]
[
2
]
=
d
p
[
i
−
1
]
[
2
]
+
d
p
[
i
−
1
]
[
1
]
dp[i][1]=dp[i-1][1]+dp[i-1][0]\\ dp[i][2]=dp[i-1][2]+dp[i-1][1]
dp[i][1]=dp[i−1][1]+dp[i−1][0]dp[i][2]=dp[i−1][2]+dp[i−1][1]
而当
a
i
=
?
a_i=?
ai=?时,
?
?
?可以转化为a
、b
、c
三种。
- 当
?
转化为a
时,a
数量增加了 3 c i − 1 3^{c_{i-1}} 3ci−1,原来的ab
、abc
数量不变 - 当
?
转化为b
时,ab
发生转移,增加了之前a
的数量,a
、bc
数量不变 - 当
?
转化为c
时,abc
发生转移,增加了之前ab
的数量,a
、ab
数量不变
而以上三种效果是叠加的,因为产生了三种新的生成串,此时的转移方程为
d
p
[
i
]
[
0
]
=
d
p
[
i
−
1
]
[
0
]
×
3
+
3
c
i
−
1
d
p
[
i
]
[
1
]
=
d
p
[
i
−
1
]
[
1
]
×
3
+
d
p
[
i
−
1
]
[
0
]
d
p
[
i
]
[
2
]
=
d
p
[
i
−
1
]
[
2
]
×
3
+
d
p
[
i
−
1
]
[
1
]
dp[i][0]=dp[i-1][0]\times 3+ 3^{c_{i-1}}\\ dp[i][1]=dp[i-1][1]\times 3+dp[i-1][0]\\ dp[i][2]=dp[i-1][2]\times 3+dp[i-1][1]
dp[i][0]=dp[i−1][0]×3+3ci−1dp[i][1]=dp[i−1][1]×3+dp[i−1][0]dp[i][2]=dp[i−1][2]×3+dp[i−1][1]
写的很迷糊的代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
//#define int ll
const int maxn=2e5+10,inf=0x3f3f3f3f,mod=1000000007;
ll qmod(ll a,ll b,ll mod) //快速幂
{
ll ans=1;
a=a%mod;
while(b)
{
if(b&1)
ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
char s[maxn];
ll dp[maxn][4];//a,ab,abc的数量
signed main(signed argc, char const *argv[])
{
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int n,cnt=0;
cin>>n>>s+1;
for(int i=1;i<=n;i++)
{//[1,i]生成段内包含的生成序列数量
if(s[i]=='a')
{//之前产生的字符串有3^cnt种,对一个串要+1
dp[i][0]=dp[i-1][0]+qmod(3,cnt,mod);//3^cnt个串都有了
// dp[i][0]=dp[i-1][0]+dp[i-1][3];//a的数量
dp[i][1]=dp[i-1][1];//ab的数量
dp[i][2]=dp[i-1][2];//abc的数量
// dp[i][3]=dp[i-1][3];//?的数量
}
else if(s[i]=='b')
{
dp[i][1]=dp[i-1][1]+dp[i-1][0];//ab的数量
dp[i][0]=dp[i-1][0];//a的数量
dp[i][2]=dp[i-1][2];//abc的数量
// dp[i][3]=dp[i-1][3];//?的数量
}
else if(s[i]=='c')
{
dp[i][2]=dp[i-1][2]+dp[i-1][1];//abc的数量
dp[i][0]=dp[i-1][0];//a的数量
dp[i][1]=dp[i-1][1];//ab的数量
// dp[i][3]=dp[i-1][3];//?的数量
}
else{//?可以为a,b,c,分成三种串
dp[i][0]=dp[i-1][0]*3+qmod(3,cnt,mod);
dp[i][1]=dp[i-1][1]*3+dp[i-1][0];//?->b
dp[i][2]=dp[i-1][2]*3+dp[i-1][1];//?->c
cnt++;
}
for(int j=0;j<4;j++)
dp[i][j]%=mod;
}
cout<<dp[n][2]<<endl;
return 0;
}
/*
如果没有?,是一个相当简单的dp
*/