zoukankan      html  css  js  c++  java
  • [SDOI2017]硬币游戏

    考虑生成函数来做

    g(x)函数就是0+0*x+...+1*x^s+...+|∑|^(n-s)x^n

    就是最后s位必须填这个串,但是前面随便填的方案数

    然后枚举之前出现了哪个串(包括自己),如果没有相交,就是fj(x)*g(x),还有就是有前后缀有相交部分,

    Pji(x)中的第k位,表示i的长度为m-k的前缀和j的长度为m-k的后缀是不是一样 0/1。

    再加上最后一个

    $sum f_i=1$因为游戏最终会停止

    现在有n+1个方程,g(x)直接当做未知数的话会有交叉项解不出来

    把$(1-|Sigma|x)$乘过去,求导来搞搞事情:

    由于带入$x=1/|Sigma|$使得这个$(1-|Sigma|x)$等于0,求导可以消去一些项

    $-|Sigma|fi=S(x)^{S-1}-Sx^{S-1}(sum_jf_j)-x^S(sum_jf'_j)+|Sigma|(sum_jf_jP_{ji})$

    把$(sum_jf'_j)$当做另外一个未知量

    就有n+1个未知量,n+1个方程了

     根据“洛必达法则”(一种极限处理)由于最后是收敛的,一定有极限,所以带入当x=1/|∑|的时候不会除以0成为很大的数(我在口胡)

    代码:
    卡精度,不用eps反而更好

    // luogu-judger-enable-o2
    #include<bits/stdc++.h>
    #define reg register int
    #define il inline
    #define ld long double 
    #define numb (ch^'0')
    using namespace std;
    typedef long long ll;
    il void rd(int &x){
        char ch;x=0;bool fl=false;
        while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
        for(x=numb;isdigit(ch=getchar());x=x*10+numb);
        (fl==true)&&(x=-x);
    }
    namespace Miracle{
    const int N=303;
    const double eps=1e-8;
    const ll mod[2]={1000000007,998244353};
    const ll jin[2]={131,13331};
    ll pw[N][2];
    ll has[N][N][2];
    char s[N];
    ld f[N][N];
    ld ans[N];
    int n,m;
    ld m0,m1;
    bool cmp(ld x){
        return 1;
    }
    void Guass(int n){
        for(reg i=1;i<=n;++i){
            int id=0;
            for(reg j=i;j<=n;++j){
                if(cmp(f[j][i])&&fabs(f[j][i])>fabs(f[id][i])) id=j;
            }
            if(id!=i){
                for(reg j=1;j<=n+1;++j){
                    swap(f[i][j],f[id][j]);
                }
            }
            for(reg j=i+1;j<=n;++j){
                if(cmp(f[j][i])){
                    ld tmp=f[j][i]/f[i][i];
                    for(reg k=1;k<=n+1;++k){
                        f[j][k]=f[j][k]-f[i][k]*tmp;
                    }
                }
            }
        }
        for(reg i=n;i>=1;--i){
            for(reg j=1;j<=n;++j){
                if(cmp(ans[j])) f[i][n+1]-=ans[j]*f[i][j];
            }
            ans[i]=f[i][n+1]/f[i][i];
        }
    }
    int main(){
        rd(n);rd(m);
        pw[0][0]=pw[0][1]=1;
        for(reg i=1;i<=m;++i){
            for(reg j=0;j<=1;++j){
                pw[i][j]=pw[i-1][j]*jin[j]%mod[j];
            }
        }
        for(reg i=1;i<=n;++i){
            scanf("%s",s+1);
            
            for(reg j=1;j<=m;++j){
                for(reg l=0;l<=1;++l){
                    has[i][j][l]=(has[i][j-1][l]*jin[l]+s[j]-'A')%mod[l];
                }
            }
        }
        m0=m1=1.0;
        for(reg i=1;i<=m;++i) m1=m1*0.5;
        m0=m1*2;
        
        for(reg i=1;i<=n;++i){
        //    cout<<" turns "<<i<<" ----------------------- "<<endl;
            for(reg j=1;j<=n;++j){
                
            //    cout<<" with "<<j<<endl;
                ld tmp=0.5;
                ld val=0;
                for(reg k=1;k<=m-1;++k){
                    int to=m-k;
                    ll bac0=(has[j][m][0]-has[j][m-to][0]*pw[to][0]%mod[0]+mod[0])%mod[0];
                    ll bac1=(has[j][m][1]-has[j][m-to][1]*pw[to][1]%mod[1]+mod[1])%mod[1];
                    
                    if(bac0==has[i][to][0]&&bac1==has[i][to][1]){
                    //    cout<<" ok "<<to<<endl;
                        val+=tmp;
                    }
                    tmp*=0.5;
                }
            //    cout<<" val "<<val<<endl;
                f[i][j]=m*m0-2.0*val;
            }
            f[i][i]-=2.0;
            f[i][n+1]=m1;
            f[i][n+2]=m*m0;
        }
        
        for(reg i=1;i<=n;++i){
            f[n+1][i]=1.0;
        }
        f[n+1][n+2]=1.0;
        
    //    for(reg i=1;i<=n+1;++i){
    //        for(reg j=1;j<=n+2;++j){
    //            cout<<f[i][j]<<" ";
    //        }cout<<endl;
    //    }
        Guass(n+1);
        
        for(reg i=1;i<=n;++i){
            printf("%.10Lf
    ",ans[i]);
        }
        return 0;
    }
    
    }
    signed main(){
        Miracle::main();
        return 0;
    }
    
    /*
       Author: *Miracle*
       Date: 2019/2/18 17:41:21
    */

     总结:

    对于连续一些位置有值的时候,
    生成函数确实很好用

    求导由于可以降次,这里就把交叉项成功分开了。

    PS:这个题也有直接上概率的做法,本质和生成函数相同。但是解释起来最好的绝对是生成函数

  • 相关阅读:
    链表 原文:http://canlynet.blog.163.com/blog/static/2550136520091120101712136/
    单向链表操作 原文:http://canlynet.blog.163.com/blog/static/255013652009113001041903/
    二叉树——根据遍历结果,画出对应的二叉树 转载至:http://canlynet.blog.163.com/blog/static/255013652009112602449178/
    数据结构经典问题——出栈顺序 转载至:canlynet微博
    sql server2005 常用语句
    禁止复制文字,下载图片的方法
    linux关机命令大全 来源于网络
    工厂模式
    设计模式的七大原则
    单例模式
  • 原文地址:https://www.cnblogs.com/Miracevin/p/10397957.html
Copyright © 2011-2022 走看看