SDOI2017硬幣遊戲
阿新 • • 發佈:2018-11-06
題面連結
sol
神題,幸好我不是SD的QAQ。
假設你們都會\(O(n^3m^3)\)的高斯消元,具體來說就是建出\(Trie\)圖然後套遊走的板子。
然後我們發現可以把不能匹配任何串的概率壓到一起。
考慮一個不能匹配任何串的\(S\)。一個串\(A_i\)獲勝當且僅當最後串是這樣的:\(S+A_i\)。
真的嗎?
如果\(S\)的字尾和\(A_i\)的字首能拼出來\(A_j\)就假掉了。所以神仙們採用了神仙做法。
引用\(Kelin\)神犇的例子。
舉個例子設\(A=101,B=110\)。
\(S101=(S+A),(S'+A+01),(S''+B+1)\),其中\(S'+10=S,S''+1=S\)
上面三種組成方式概率為\(2\)的他們後面串的長度次方,分別是\(1,\frac{1}{4},\frac{1}{2}\)。
於是一個上好的方程就列出來了。
\[\frac{1}{8}P_S=(1+\frac{1}{4})P_A+\frac{1}{2}P_B\]。
由於這種辣雞題目你直接消肯定是錯的的定律這些方程一定有\(n\)個可以線性張成另一個,所以我們還要加上\(\sum\limits_{i=1}^nP_i=1\)。
畢竟我們什麼都不加的化每個\(P_i\)擴大相同倍數也是對的QAQ。
就醬。
#include<cstdio> #include<cstring> #include<algorithm> #define gt getchar() #define ll long long #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) inline int in() { int k=0;char ch=gt; while(ch<'-')ch=gt; while(ch>'-')k=k*10+ch-'0',ch=gt; return k; } const int N=305,M=1e5+5; const double eps=1e-10,P=0.5; int n,m,cnt,ch[M][2],head[M],to[M],nxt[M]; int pos[N],fa[M],sz[M],tot; double p[N],G[N][N]; char s[N]; inline void add(int u,int v){to[++cnt]=v,nxt[cnt]=head[u],head[u]=cnt;} #define v (ch[u][i]) inline void insert(int p) { scanf("%s",s+1);int u=0,i; for(int j=1;j<=m;++j)i=s[j]=='H',sz[!v?v=++tot:v]=sz[u]+1,add(u=v,p); pos[p]=u; } inline void build() { static int q[M];int h=1,t=0,u=0,i; for(int i=0;i<=1;++i)if(v)q[++t]=v; while(h<=t)for(u=q[h++],i=0;i<2;++i)v?fa[q[++t]=v]=ch[fa[u]][i]:v=ch[fa[u]][i]; } #undef v inline void calc(int x) { for(int u=pos[x];u;u=fa[u]) for(int i=head[u];i;i=nxt[i]) G[to[i]][x]+=p[m-sz[u]]; } int o[N]; inline void Gauss(int n) { for(int i=1;i<=n;++i) { pos[i]=0; for(int j=1;j<=n;++j)if(!o[j]&&G[j][i]){pos[i]=j;break;} o[pos[i]]=1;double t=G[pos[i]][i]; for(int j=1;j<=n+1;++j)G[pos[i]][j]/=t; for(int k=1;k<=n;++k) if(pos[i]!=k) { t=G[k][i]; for(int j=1;j<=n+1;++j)G[k][j]-=G[pos[i]][j]*t; } } } int main() { n=in(),m=in();p[0]=1;for(int i=1;i<=m;++i)p[i]=p[i-1]*P; for(int i=1;i<=n;++i)insert(i);build(); for(int i=1;i<=n;++i)calc(i); for(int i=1;i<=n;++i)G[i][n+1]=-p[m],G[n+1][i]=1,G[n+1][n+2]=1; Gauss(n+1); for(int i=1;i<=n;++i)printf("%.10lf\n",G[pos[i]][n+2]); return 0; }