您的位置:首页 > 其它

[BZOJ4820][SDOI2017]硬币游戏(KMP+概率+高斯消元)

2018-03-31 20:40 411 查看

题目:

我是超链接

题解:

这种东西是概率dp嘛,看个数据范围似乎还要扯上和概率经常玩耍的高斯消元

这个题目的关键是设一个变量P(N)P(N)表示所有没有到达终止点的概率

假如说A同学猜的是TTH,B同学猜的是HTT,设P(A)P(A)表示A取胜的概率,P(B)P(B)同理

那么如果是N没有达到终止点,加上一个TTH肯定就结束了嘛,因为A赢了,但是在A赢的路上B可能已经赢了,比如说N如果已经有了H或者HT,那么赢的就是B了,那么我们可以列出P(N+TTH)=P(A)+P(B+TT)+P(B+T)P(N+TTH)=P(A)+P(B+TT)+P(B+T)

化简一下就是0.125P(N)=P(A)+0.25P(B)+0.5P(B)=P(A)+0.75P(B)0.125P(N)=P(A)+0.25P(B)+0.5P(B)=P(A)+0.75P(B)

这个东西可以用来构造系数诶,我们发现有前缀和后缀有相同的两个字符串可以有系数关系

怎么构造系数呢?kmp/AC自动机都OK的,KMP好写一些

有了系数就可以高斯消元了,针对n个人n种系数方程再加上所有可能相加为1,一共n+1个未知数,n+1个方程

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N=350;
char w[N*2],st

;int len,t[N*2];
double a

,b
,ans
,mi
;
void kmp()
{
t[0]=-1;
for (int i=0;i<len;i++)
{
int j=t[i];
while (j!=-1 && w[i]!=w[j]) j=t[j];
t[i+1]=j+1;
}
}
void gauss(int n)
{
n++;
for (int i=1;i<=n;i++)
{
int k=i;
for (int j=i+1;j<=n;j++)
if (a[j][i]>a[k][i]) k=j;
for (int j=i;j<=n;j++) swap(a[i][j],a[k][j]);
swap(b[i],b[k]);
for (int j=i+1;j<=n;j++)
{
double t=a[j][i]/a[i][i];
for (int k=i;k<=n;k++) a[j][k]-=t*a[i][k];
b[j]-=t*b[i];
}
}
for (int i=n;i>=1;i--)
{
ans[i]=b[i]/a[i][i];
for (int j=1;j<i;j++) b[j]-=ans[i]*a[j][i];
}
}
int main()
{
int n,m;scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%s",st[i]+1);
mi[0]=1.0;for (int i=1;i<=m;i++) mi[i]=mi[i-1]*0.5;
for (int i=1;i<=n;i++)
{
a[i][n+1]=-mi[m];
int lj=0;
for (int k=1;k<=m;k++) w[lj++]=st[i][k];w[lj++]='&';
for (int j=1;j<=n;j++)
{
len=lj;
for (int k=1;k<=m;k++) w[len++]=st[j][k];
kmp();
int now=t[len];
while (now>0)
{
a[i][j]+=mi[m-now];
now=t[now];
}
}
}
for (int i=1;i<=n;i++) a[n+1][i]=1.0;b[n+1]=1.0;
gauss(n);
for (int i=1;i<=n;i++) printf("%.10lf\n",ans[i]);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: