1. 程式人生 > 其它 ><題解>[SDOI2017]硬幣遊戲

<題解>[SDOI2017]硬幣遊戲

solutions

題面(loj)

題面(luogu)

這個題吧是我很久很久以前留下的坑了,到了今天才補好。(是不是太菜了

暴力

這個和之前的題解一樣,確實可以用 trie 樹,這複雜度是\(\mathcal{O(n^3m^3)}\)

您就是初學OI,也不應該看到資料範圍之後想到這樣的複雜度。

所以這個暴力就直接捨棄吧。

但是如果別的題用到了,還是可以試一試的,畢竟考試的時候部分分還是比較重要的。

正解

不知道咱也不知道為啥這個題的思路可以如此的妙。

首先考慮,每一個合法的能夠讓一個玩家贏的字串序列一定是由一個啥也匹配不上的序列和當前玩家的序列構成並且當前玩家的序列是整個序列的字尾。

但是你發現,完全無法找全這樣的每一個序列,並且序列是無限個的。

假如我當前有一個序列 S ,那麼我可以得到一個合法的序列。

設這個合法的序列為 \(N=S+a_j\)\(a_j\) 為每一個玩家的序列。

可能你會認為,這樣不就是 \(j\) 玩家獲勝的概率了????

你錯了,你不知道這個 \(S\) 裡面會發生什麼,也許這個 \(S\) 中已經滿足了另外一個玩家的序列

所以一切都在不可測之中。

但是你並不關心前面的序列究竟是什麼,因為他的末尾一定是 \(a_j\)

這樣的話我們就需要把前面所有的情況都算上,全部!!!

比如說我們這裡有兩個人 \(HT\)\(TH\)

那麼我們就有這樣一個不太好的序列 \(HTH\) ,這種序列就是我所說的那種壞壞的序列

雖然他是以 \(TH\) 結尾的,但是他前面包含了 \(HT\) 這個序列,所以我們會發現,

在以 \(TH\) 結尾的所有序列中(前面可以加入任意多個字元,當然也可以不加),

我們發現,這種序列的貢獻由兩部分組成,一部分是 \(TH\) 結尾的貢獻,

另一部分就是 \(HT\) 結尾的貢獻,那麼現在最大的問題就是如何計算這個貢獻。

你發現這時候想著想著正解就在眼前了。

這時候你不確定這些序列會造成多少貢獻,就是你不知道這些以玩家序列結尾的序列的貢獻

所以這裡也是一個未知數N,那其他的玩家勝利的概率 \(x_i\) ,這時候我們已經找到了 \(n+1\) 個未知數了。

接下來的任務就是如何求解這些未知數了,一般遇到這麼多未知數,一眼就是高斯消元。

所以我們就要尋找這些未知數之間的關係。

第一個方程,一定會有一個玩家勝利,那麼 \(\sum\limits_{i=1}^{n}x_i=1\)

根據剛才我們的分析,我們發現,對於以玩家序列結尾的序列可以根據每兩個玩家的序列分為很多部分,

這個序列出現的概率就是 \(\frac{1}{n}N\) 而如果當前玩家的字首和其他玩家的字尾相同,

那這個概率就可以表示為這些玩家勝出的概率再乘上出現當前情況的概率,

我們設 \(fro_{ij}\) 表示 \(i\) 玩家的長度為 \(j\) 的字首,字尾同樣處理為 \(beh_{ij}\)

於是我們就有 \(\frac{1}{n}N=\sum\limits_{i=1}^{n}x_i\sum\limits_{j=1}^{n}\sum\limits_{k=0}^{m}[fro_{ik}==beh_{jk}]\frac{1}{2^{m-k}}\)

這時候我們就有 \(n+1\) 條方程了,就是直接高斯消元即可

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ull unsigned long long
#define double long double
const int N=305;
int n,m;
char ch[N][N];
ull hs[N][N],bas=131,ba[N];
double a[N][N],x[N],mse[N];
signed main(){
	scanf("%d%d",&n,&m);
	ba[0]=1;mse[0]=1;
	for(re i=1;i<=m;i++)
		ba[i]=ba[i-1]*bas,
		mse[i]=mse[i-1]/2.0;
	for(re i=1;i<=n;i++){
		scanf("%s",ch[i]+1);
		for(re j=1;j<=m;j++)
			hs[i][j]=hs[i][j-1]*bas+ch[i][j];
	}
	for(re i=1;i<=n;i++){
		for(re j=1;j<=n;j++){
			double ans=0;
			for(re k=0;k<=m;k++){
				ull tmpf=hs[i][k];
				ull tmpb=hs[j][m]-hs[j][m-k]*ba[k];
				if(tmpf==tmpb)ans+=mse[m-k];//cout<<k<<" ";
			}
			//cout<<endl;
			//cout<<ans<<" "<<mse[0]<<endl;
			a[i][j]=ans;
		}
		a[i][n+1]=-mse[m];
	}
	for(re i=1;i<=n;i++)a[n+1][i]=1.0;
	a[n+1][n+2]=1.0;n++;
	int h,z;
	for(h=1,z=1;h<=n&&z<=n;h++,z++){
		int maxn;
		for(re i=h;i<=n;i++)
			if(a[i][z]!=0){
				maxn=i;break;
			}
		if(maxn!=h)
			for(re i=1;i<=n+1;i++)
				swap(a[maxn][i],a[h][i]);
		//if(fabs(a[h][z])==0){
		//	h--;continue;
		//`}
		for(re i=h+1;i<=n;i++)
			if(a[i][z]!=0){
				double t=a[i][z]/a[h][z];
				for(re j=z;j<=n+1;j++)
					a[i][j]-=a[h][j]*t;
			}
	}
	//cout<<h<<" "<<z<<endl;
	for(re i=n;i>=1;i--){
		double t=a[i][n+1];
		for(re j=n;j>i;j--)
			t-=a[i][j]*x[j];//cout<<t<<" "<<a[i][j]<<endl;
		x[i]=t/a[i][i];
		//cout<<t<<endl;
	}
	for(re i=1;i<=n-1;i++){
		printf("%.10Lf\n",x[i]);
	}
}