Codeforces Round #674 (Div. 3) F Number of Subsequences

上课打了一下,菜的真实,直接爆零QAQ。
并不是很难的一场div3,我甚至觉得一个擅长dp的rating1800+选手这场可以AK。
E是一个超简单的最大流问题,可惜当时没看,一直卡D。
D题大体方向没错,如果能冷静分析,就能发现贪心的方法了,可惜并没有。
C是一个均值不等式,我没看出来。
这里补一下F题计数dp,因为F题确实不能自己写出来,立篇博客分析一下。

题意

给定一个由abc?符号构成的字符串,你可以用abc替换?,求由给定的字符串 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[i1][0]+1

则包含?时,要考虑此时已有多少个生成串,可以发现生成串的数目只和?的数目有关,记到 i i i?的数目为 c i c_i ci,则以有的生成串数目为 3 c i − 1 3^{c_{i-1}} 3ci1

则当 s i = a s_i=a si=a时,对于所有的 3 c i − 1 3^{c_{i-1}} 3ci1个生成串,一共增加了 3 c i − 1 3^{c_{i-1}} 3ci1 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[i1][1]+dp[i1][0]dp[i][2]=dp[i1][2]+dp[i1][1]
而当 a i = ? a_i=? ai=?时, ? ? ?可以转化为abc三种。

  • ?转化为a时,a数量增加了 3 c i − 1 3^{c_{i-1}} 3ci1,原来的ababc数量不变
  • ?转化为b时,ab发生转移,增加了之前a的数量,abc数量不变
  • ?转化为c时,abc发生转移,增加了之前ab的数量,aab数量不变

而以上三种效果是叠加的,因为产生了三种新的生成串,此时的转移方程为
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[i1][0]×3+3ci1dp[i][1]=dp[i1][1]×3+dp[i1][0]dp[i][2]=dp[i1][2]×3+dp[i1][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
*/
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值