bzoj 4820: [Sdoi2017]硬币游戏【kmp+高斯消元】

有点神,按照1444的做法肯定会挂
注意到它的概率是相同的,所以可以简化状态
详见http://www.cnblogs.com/candy99/p/6701221.html
https://www.cnblogs.com/liu-runda/p/6919077.html
总之就是靠在kmp中的ne数组上跳来找到对当前串概率有影响的串然后把概率加起来……

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const int N=305;
int n,m,ne[N][N];
double a[N][N],p[N];
char c[N][N];
void gtne(char s[],int ne[])
{
	for(int i=2,j=0;i<=m;i++)
	{
		while(j&&s[j+1]!=s[i])
			j=ne[j];
		if(s[j+1]==s[i])
			j++;
		ne[i]=j;
	}
}
void gaosi(int n)
{
	for(int i=1;i<=n;i++)
	{
		int nw=i;
		for(int j=i+1;j<=n;j++)
			if(fabs(a[j][i])>fabs(a[nw][i]))
				nw=j;
		for(int j=i;j<=n+1;j++)
			swap(a[i][j],a[nw][j]);
		for(int j=i+1;j<=n+1;j++)
			a[i][j]/=a[i][i];
		a[i][i]=1;
		for(int j=i+1;j<=n;j++)
		{
			for(int k=i+1;k<=n+1;k++)
				a[j][k]-=a[j][i]*a[i][k];
			a[j][i]=0;
		}
	}
	for(int i=n-1;i>=1;i--)
	{
		for(int j=i+1;j<=n;j++)
			a[i][n+1]-=a[j][n+1]*a[i][j];
		a[i][n+1]/=a[i][i];
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	p[0]=1;
	for(int i=1;i<=m;i++)
		p[i]=p[i-1]/2;
	for(int i=1;i<=n;i++)
		scanf("%s",c[i]+1),a[i][n+1]=-p[m],a[n+1][i]=1;
	a[n+1][n+2]=1;
	for(int i=1;i<=n;i++)
		gtne(c[i],ne[i]);
	for(int y=1;y<=n;y++)
		for(int x=1;x<=n;x++)
		{
			int j=0;
			for(int i=1;i<=m;++i)
			{
				while(j&&c[y][j+1]!=c[x][i]) 
					j=ne[y][j];
				if(c[y][j+1]==c[x][i]) 
					j++;
			}
			while(j) 
				a[y][x]+=p[m-j],j=ne[y][j];
		}
	gaosi(n+1);
	for(int i=1;i<=n;i++)
		printf("%.10f\n",a[i][n+2]);
	return 0;
}
posted @ 2018-09-07 20:25  lokiii  阅读(135)  评论(0编辑  收藏  举报