A
因为太水,我不想写题解,Yvehの传送门
B
思路:
很强的一道题目(聪哥说是概率入门题……)
昨天打比赛时懵逼了很久
我们先考虑一个简单的问题,如果
a[i]=0
,怎么做?
一道比较经典的题目(51nod 1202),给定序列
s
,求不同的子序列个数
但这是在
s[i]
没有出现过的情况下的方程
那如果
s[i]
之前已经出现过呢?
设
s[i]
最后出现的位置是
last
,那么方程就是
f[i]=f[i−1]×2−f[last−1]
我们可以这么想,这时重复的串就是last之前的子序列分别与 s[last] , s[i] 相匹配,这些子序列的数量就是 f[last−1]
这样就可以在 O(n) 时间内递推出 f[n] 了
但是这里加入了概率,我们怎么在这上面改进呢?
我先给出方程,下面再解释
a[i] 表示第i位被删除的概率, f[i] 表示到第 i 位时本质不同的子序列的期望,
咦,好像 sum[i] 的定义很诡异啊,我们来重新断下句(。・∀・)ノ゙
sum[i] 表示
i 本身及
的前一位
的期望
的前缀和
如果没看懂就继续往下看好了= =
f[i]=(f[i−1]×2−sum[last])×(1−a[i])+f[i−1]×a[i]
sum[i]=f[i−1]×(1−a[i])+sum[last]×a[i]
考虑第 i 位时,如果第
sum[i] 的求法也很简单,类似 f[i] ,如果 i 没被删除,那就是
乘下相应的概率就可以了
由于要取模,所以先求下 100 关于 998244353 的逆元
时间复杂度和空间复杂度都是 O(n)
代码:
#include<cstdio>
#include<iostream>
#define M 500003
#define mo 998244353
#define LL long long
using namespace std;
int n;
char s[M];
int a[M],last[27],f[M],sum[M];
int qr(int x,int y)
{
int t=1;
for (;y;y>>=1,x=(LL)x*x%mo)
if (y&1) t=(LL)t*x%mo;
return t;
}
main()
{
scanf("%d",&n);
scanf("%s",s+1);
for (int i=1;i<=n;++i) scanf("%d",a+i);
int inv=qr(100,mo-2);
f[0]=1;
for (int i=1;i<=n;++i)
{
int t=sum[last[s[i]-'a']];
f[i]=((100-a[i])*(2LL*f[i-1]%mo-t)%mo+(LL)a[i]*f[i-1]%mo)%mo*inv%mo;
sum[i]=((LL)f[i-1]*(100-a[i])%mo+(LL)t*a[i]%mo)*inv%mo;
last[s[i]-'a']=i;
}
printf("%d\n",(f[n]+mo)%mo);
}
C
因为太神太麻烦不想写题解了
Yvehの传送门