CF1426 F. Number of Subsequences

题目链接:http://codeforces.com/contest/1426/problem/F

描述:

给出一个由a,b,c,?组成的字符串S,?可以变为a,b,c其中任何一个,求在s的所有可能情况中会有多少子序列abc。

3≤n≤200000

分析:

a[i]表示到i一共有多少‘a’,b[i],c[i],d[i]分别是'b','c','?'。若没有'?',则只需要在每个b出现的位置i,以该b为中心计算贡献,即ans+=a[i-1]*(c[n]-c[i]);加上'?'以后,分为四种情况。a???b??c

1.组成abc中没有问号参与,此每个?有三种可能,所以ans+=3^d[n]

2.ab?,组成的abc类型为此,所以b之后的?任取一个变成c,剩下的所有?由三种可能,所以ans+=a[i-1]*3^(d[n]-1)*(d[n]-d[i])

3.?bc,组成的abc类型为此,与2类似,所以ans+=(c[n]-c[i])*3^(d[n]-1)*d[i-1];

4.?b?,组成的abc类型为此,b之前和后面各取一个?变为a,c,剩下有3种可能,所以ans=(ans+3^(d[n]-2)*d[i-1]*(d[n]-d[i]))

#include<bits/stdc++.h>
#define N 200010
#define ll long long
using namespace std;
ll a[N],b[N],c[N],d[N],e[N];
char s[N];
int n;
const int md=1e9+7;
ll calc(){
	ll ret=0;
	a[0]=0;b[0]=0;c[0]=0;d[0]=0;
	for(int i=1;i<=n;i++){
		a[i]=a[i-1]+(s[i]=='a');
		b[i]=b[i-1]+(s[i]=='b');
		c[i]=c[i-1]+(s[i]=='c');
		d[i]=d[i-1]+(s[i]=='?');
	}
	for(int i=1;i<=n;i++)
	if(s[i]=='b')ret=(ret+a[i-1]*(c[n]-c[i])%md)%md;
	return ret;
}
ll ans=0;
void dfs(int x){
	if(x>n){
		ans=(ans+calc())%md;
		return;
	}
	if(s[x]=='?'){
		s[x]='a';
		dfs(x+1);
		s[x]='b';
		dfs(x+1);
		s[x]='c';
		dfs(x+1);
	}else dfs(x+1);
}
int main(){
	scanf("%d",&n);
	scanf("%s",s+1);
	e[0]=1;
	for(int i=1;i<=n;i++)e[i]=e[i-1]*3%md;
	ans=0;
	a[0]=0;b[0]=0;c[0]=0;d[0]=0;
	for(int i=1;i<=n;i++){
		a[i]=a[i-1]+(s[i]=='a');
		b[i]=b[i-1]+(s[i]=='b');
		c[i]=c[i-1]+(s[i]=='c');
		d[i]=d[i-1]+(s[i]=='?');
	}
	for(int i=2;i<n;i++){
		if(s[i]=='b'){
			ans=(ans+a[i-1]*(c[n]-c[i])%md*e[d[n]]%md)%md;
			if(d[n]>=1)ans=(ans+a[i-1]*e[d[n]-1]*(d[n]-d[i])%md)%md;
			if(d[n]>=1)ans=(ans+(ll)(c[n]-c[i])*e[d[n]-1]*d[i-1]%md)%md;
			if(d[n]>=2)ans=(ans+(ll)e[d[n]-2]*d[i-1]%md*(d[n]-d[i])%md)%md;
		//	cout<<ans<<endl;
		}if(s[i]=='?'){
			if(d[n]>=1)ans=(ans+(ll)a[i-1]*(c[n]-c[i])%md*e[d[n]-1]%md)%md;	
			if(d[n]>=2)ans=(ans+(ll)a[i-1]*e[d[n]-2]*(d[n]-d[i])%md)%md;
			if(d[n]>=2)ans=(ans+(ll)(c[n]-c[i])*e[d[n]-2]*d[i-1]%md)%md;
			if(d[n]>=3)ans=(ans+e[d[n]-3]*d[i-1]%md*(d[n]-d[i])%md)%md;	
		
		}
	}
	printf("%lld\n",ans);
//	ans=0;
	//dfs(1);cout<<ans;
	return 0;
}

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值