zoukankan      html  css  js  c++  java
  • CF1556F Sports Betting (状压枚举子集DP)

    F

    对于一张比赛图,经过缩点,会得到dag,且它一定是transitive的,因此我们能直接把比赛图缩成一个有向链。链头作为一个强连通分量,里面的所有点都是胜利的

    定义F(win)表示win集合作为赢家的概率,我们有

    [ans=sum_{winin all} F(win)|win| ]

    显然win集合内的点构成一个强连通分量,并作为链头。win集合内的点一定向集合外的每个点连边

    考虑如何求解F(win)

    我们定义H(win)表示在win集合内的点构成的子图中,win集合成为一个强连通分量的概率。

    容易发现这个子图仍然满足比赛图的性质!

    考虑去掉win不是一个强连通分量的情况,那么一定存在win的非空真子集sub,sub是子图链头,此时 sub内的所有点 向 win/sub内的所有点 连边。我们得到

    [H(win)=1-sum_{subsubset win,sub e phi}H(sub)G(sub,win/sub) ]

    其中(G(x,y))表示x集合向y集合的点直接连边的概率

    求出的H可以推F了,win想每个集合外的点连边

    [F(win)=G(win,all/win)H(win) ]

    接下来考虑如何求出G

    原题n=14,可以接受(O(n3^{n}))的做法,我们预处理出每个点连向一个集合的概率(f(i,s)),那么每次调用G的时间是(O(n))

    题解提供了一个(O(3^{n}))的方法,让每次调用G是(O(1))

    首先是预处理每个点连向集合的概率,我们处理出每个数最高位的1的位置,就可以在(O(n2^{n}))递推出(f(i,s))

    考虑把n分成大小不超过1的两个集合left,right

    现在有一个询问(G(x,y)),x,y分别在left和right集合内存在一部分lx,ly,rx,ry,答案是

    (G(x,y)=G(lx,ly)G(lx,ry)G(rx,ly)G(rx,ry))

    这四部分贡献可以分别预处理,由于left和right的大小只有原来的一半,每个数组处理的时间只有(O(n2^{n}))

    tips:经可可提醒,这个优化套路在[ctsc2017]吉夫特,有机会去做一下

    #include<bits/stdc++.h>
    
    using namespace std;
    
    #define r(x) read(x)
    #define gc c=getchar()
    #define ll long long
    #define ffl fflush(stdout)
    #define it set<string>::iterator 
    
    const int N1=(1<<14)+5, p=1e9+7, maxn=2e6;
    const int M1=(1<<7)+5;
    const ll inf=0x3f3f3f3f3f3f3fll;
    template<typename T>
    inline void read(T &x){
        x=0;T k=1;char gc;
        while(!isdigit(c)){if(c=='-')k=-1;gc;}
        while(isdigit(c)){x=x*10+c-'0';gc;}x*=k;
    }
    
    int n,m,L,R;
    int a[16],bcnt[N1],lg[N1];
    ll F[N1],f[16][N1],H[N1],inv[maxn+5];
    ll gl[M1][M1],gr[M1][M1],glr[M1][M1],grl[M1][M1];
    
    ll G(int X,int Y){
        int lx=X&L, rx=(X&R)>>m, ly=Y&L, ry=(Y&R)>>m;
        return gl[lx][ly]*gr[rx][ry]%p*glr[lx][ry]%p*grl[rx][ly]%p;
    }
    // inline int lowbit(int x){ return x&(-x); }
    
    int main(){
        // freopen("1.in","r",stdin);
        // freopen(".out","w",stdout);
        // int Te; read(Te);
        // while(Te--) printf("%lld
    ",solve());
        read(n); 
        if(n==1){ puts("1"); return 0; }
        for(int i=0;i<n;i++) read(a[i]);
        inv[1]=1;
        for(int i=2;i<=maxn;i++) inv[i]=1ll*(p-p/i)*inv[p%i]%p;
        int all=(1<<n)-1;
        lg[1]=0;
        for(int i=2;i<(1<<n);i++) lg[i]=lg[i>>1]+1;
        for(int s=1;s<(1<<n);s++) bcnt[s]=bcnt[s>>1]+(s&1);
        // for(int s=0;s<(1<<n);s++){
        //     for(int i=0;i<n;i++) if( !((1<<i)&s) ){
        //         f[i][s]=1;
        //         for(int j=0;j<n;j++) if((1<<j)&s) (f[i][s]*=a[i]*inv[a[i]+a[j]]%p)%=p;
        //     }
        // }
        //每次去掉highbit 也就是lg 可以O(n*2^n)内递推
        for(int i=0,j;i<n;i++){
            f[i][0]=1;
            for(int s=1;s<(1<<n);s++) if(!((1<<i)&s)){
                j=lg[s];
                f[i][s]=f[i][s^(1<<j)]*a[i]%p*inv[a[i]+a[j]]%p;
            }
        }
        
        m=(n+1)/2; L=(1<<m)-1; R=all^L;
        for(int lx=0;lx<=L;lx++){
            int s=L^lx;
            for(int t=s;;t=(t-1)&s){
                gl[lx][t]=1;
                for(int i=0;i<m;i++) if((1<<i)&lx) 
                    (gl[lx][t]*=f[i][t])%=p;
                if(!t) break;
            }
        } 
        for(int rx=0;rx<=(R>>m);rx++){
            int s=(R>>m)^rx;
            for(int t=s;;t=(t-1)&s){
                gr[rx][t]=1;
                for(int i=0;i<n-m;i++) if((1<<i)&rx)
                    (gr[rx][t]*=f[i+m][t<<m])%=p;
                if(!t) break;
            }
        }
        for(int lx=0;lx<=L;lx++){
            for(int ry=0;ry<=(R>>m);ry++){
                glr[lx][ry]=1;
                for(int i=0;i<m;i++) if((1<<i)&lx)
                    (glr[lx][ry]*=f[i][ry<<m])%=p;
            }
        }
        for(int rx=0;rx<=(R>>m);rx++){
            for(int ly=0;ly<=L;ly++){
                grl[rx][ly]=1;
                for(int i=0;i<n-m;i++) if((1<<i)&rx)
                    (grl[rx][ly]*=f[i+m][ly])%=p;
            }
        }
        
        ll ans=0;
        for(int win=1;win<(1<<n);win++){
            H[win]=1; ll tmp;
            for(int sub=(win-1)&win;sub;sub=(sub-1)&win){
                // tmp=1;
                // for(int i=0;i<n;i++) if((1<<i)&sub){
                //     (tmp*=f[i][win^sub])%=p;
                // }
                H[win]=(H[win]-H[sub]*G(sub,win^sub)%p+p)%p;
            }
            // tmp=1;
            // assert(bcnt[win]!=2||H[win]==0);
            // for(int i=0;i<n;i++) if((1<<i)&win) (tmp*=f[i][all^win])%=p;
            // F[win]=tmp*H[win]%p;
            F[win]=G(win,all^win)*H[win]%p;
            (ans+=F[win]*bcnt[win]%p)%=p;
        }
        printf("%lld
    ",ans);
        return 0;
    }
    
    
  • 相关阅读:
    AJAX请求头Content-type
    原 layer父子页面交互
    layer.closeAll()无法关闭弹窗的解决办法之一
    成员函数的重载,覆盖与隐藏
    const限定符用法汇总
    构造函数和析构函数的调用时机
    MFC 消息映射、分派和传递
    C++对象模型
    函数指针与虚函数表
    数据类型与内存映像
  • 原文地址:https://www.cnblogs.com/guapisolo/p/15208137.html
Copyright © 2011-2022 走看看