简单题还能写挂,感觉没救了啊 /fd /fd /fd

题解

由于保证每个点至少有一个出边,所以操作次数等于走的边数+1。

由于这个步数可能到达 $\infty$,对于这种问题算期望的一种方法是:对于所有还没有停止的情况,算它们的概率的和。

所以考虑一个暴力的 dp:设 $f_{i,j,k}$ 表示当前走了 $i$ 步(注意这里的步是指尝试加入了几次边,而不是走过的边数,最后答案减一即可),已经加入了 $j$ 条边,现在在点 $k$ 的概率。如果能预处理出 $h_i$ 表示 $n$ 个点 $i$ 条边的非连通图在所有 $i$ 条边的图中的概率,就可以用 $\sum_{i,j,k} f_{i,j,k} \times h_j$ 来统计答案了。但是这里 $i$ 没有限制,所以状态就是 $O(\infty)$ 的(

先考虑如何转移:根据定义可以得到:

$$ \begin{align} f_{0,0,1} &= 1\\ f_{i,j,k} &= \sum_{(v,k) \in E} (f_{i-1,j,v}\times (1-p_v) + f_{i-1,j-1,v} \times p_v) \times \frac{1}{out_v} \end{align} $$

考虑尝试去掉第一维。设 $g_{i,j} = \sum_{x} f_{x,i,j}$,考虑如何对 $g_{i,j}$ 转移,显然有:

$$ g_{i,j} = \sum_{(v,j) \in E} (g_{i,v} \times (1-p_v) + g_{i-1,v} \times p_v) \times \frac{1}{out_v} + [i = 0 \and j = 1] $$

后面那个加号是为了修正 $f_{0,0,1}$ 的情况。

直接高斯消元可以达到 $O(n^9)$ 的复杂度。

一种经典优化是我们按照 $i$ 分类进行优化,我们处理到 $i$ 的时候一定已经知道了 $g_{i-1,*}$ 的值,所以每一次消元只有 $O(n)$ 个变量,直接做复杂度是 $O(n^5)$ 的。

高斯消元实际上是求解 $Ax = b$,不难发现每次变化的只有 $b$,所以我们预处理 $A^{-1}$,每次就可以 $O(n^2)$ 直接求出 $x = A^{-1}b$ 了,复杂度 $O(n^4)$。

现在考虑如何计算那个 $h$。这其实就是前几天的模拟。设 $f_{i,j}$ 表示 $i$ 个点 $j$ 条边的无向连通图个数,显然 $h_i = \binom{\binom n 2}{i} - f_{n,i}$。

暴力转移:

$$ f_{i,j} = \binom{\binom n 2}{i} - \sum_{k=1}^{i-1} \binom{i-1}{k-1} \sum_{x=0}^{j} f_{k,x} \binom{\binom{i-k}{2}}{j-x} $$

考虑写成 Poly 的形式:设 $F_i$ 是 $f_{i,j}$ 的生成函数,转移可以写成:

$$ F_i = (1+x)^{\binom n 2} - \sum_{k=1}^{i-1} \binom{i-1}{k-1} F_k \times (1+x)^{\binom {i-k}{2}} $$

将 $F$ 看成关于 $(1+x)$ 的函数,需要转移 $O(n)$ 次,每次要进行 $n$ 次多项式平移,复杂度 $O(n^4)$。最后手动展开 $(1+x)^i$ 就好了。

写一下我怎么写挂的:入度出度写反了,转移的时候拿的 $out_k$ 而不是 $out_v$,输了两次,双输!

代码

#include <bits/stdc++.h>

#define fi first
#define se second
#define DB double
#define U unsigned
#define P std::pair
#define LL long long
#define LD long double
#define pb emplace_back
#define MP std::make_pair
#define SZ(x) ((int)x.size())
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl

const int MAXN = 100 + 5;
const int MAXM = 1e5 + 5;
const int ha = 1e9 + 7;

inline int qpow(int a,int n=ha-2){
    int res = 1;
    while(n){
        if(n&1) res = 1ll*res*a%ha;
        a = 1ll*a*a%ha;
        n >>= 1;
    }
    return res;
}

int n,m;
int p[MAXN];
int fac[MAXM],inv[MAXM];

inline void add(int &x,int y){
    x += y-ha;x += x>>31&ha;
}

inline int C(int n,int m){
    return n < 0 || m < 0 || n < m ? 0 : 1ll*fac[n]*inv[m]%ha*inv[n-m]%ha;
}

std::vector<int> G[MAXN];// in edge
int f[MAXN][MAXN*MAXN];// sum aj(x+1)^j
int g[MAXN*MAXN];// i edge, unconnected

int A[MAXN][MAXN],B[MAXN][MAXN],vec[MAXN],b[MAXN];
int invm[MAXN];

inline void Inv(){
    FOR(i,1,n) B[i][i] = 1;
    FOR(i,1,n){
        int ps = -1;
        FOR(j,i,n) if(A[j][i]){ps = j;break;}
        if(ps != i) std::swap(A[ps],A[i]),std::swap(B[ps],B[i]);
        int inv = qpow(A[i][i]);
        FOR(j,1,n) A[i][j] = 1ll*A[i][j]*inv%ha,B[i][j] = 1ll*B[i][j]*inv%ha;
        FOR(j,1,n){
            if(j == i) continue;
            int d = A[j][i];
            FOR(k,1,n){
                add(A[j][k],ha-1ll*A[i][k]*d%ha);
                add(B[j][k],ha-1ll*B[i][k]*d%ha);
            }
        }
    }
}

int main(){
    fac[0] = 1;FOR(i,1,MAXM-1) fac[i] = 1ll*fac[i-1]*i%ha;
    inv[MAXM-1] = qpow(fac[MAXM-1]);ROF(i,MAXM-2,0) inv[i] = 1ll*inv[i+1]*(i+1)%ha;
    scanf("%d%d",&n,&m);int i100 = qpow(100);
    FOR(i,1,n) scanf("%d",p+i),p[i] = 1ll*p[i]*i100%ha;
    FOR(i,1,m){
        int u,v;scanf("%d%d",&u,&v);
        G[v].pb(u);++invm[u];
    }
    f[1][0] = 1;
    FOR(i,2,n){
        f[i][i*(i-1)/2] = 1;
        FOR(j,1,i-1){
            int c = C(i-1,j-1),off = (i-j)*(i-j-1)/2;
            FOR(k,off,i*i) add(f[i][k],ha-1ll*f[j][k-off]*c%ha);
        }
    }
    FOR(i,1,n) invm[i] = qpow(invm[i]);
    FOR(i,0,n*(n-1)/2) g[i] = C(n*(n-1)/2,i);
    FOR(i,0,n*(n-1)/2){
        int c = 0;
        FOR(j,i,n*(n-1)/2) add(c,1ll*f[n][j]*C(j,i)%ha);
        add(g[i],ha-c);
        g[i] = 1ll*g[i]*qpow(C(n*(n-1)/2,i))%ha;
    }
    FOR(i,1,n){
        for(auto x:G[i]) add(A[i][x],1ll*(1-p[x]+ha)%ha*invm[x]%ha);
        add(A[i][i],ha-1);
    }
    Inv();
    int ans = 0;
    FOR(i,0,n*(n-1)/2){
        FOR(v,1,n){
            b[v] = 0;
            for(auto x:G[v]) add(b[v],1ll*vec[x]*p[x]%ha*invm[x]%ha);
            b[v] = 1ll*b[v];
            if(i == 0 && v == 1) add(b[v],1);
            b[v] = (ha-b[v])%ha;
        }
        FOR(j,1,n){
            vec[j] = 0;
            FOR(k,1,n) add(vec[j],1ll*B[j][k]*b[k]%ha);
        }
        int sm = 0;
        FOR(j,1,n) add(sm,vec[j]);
        add(ans,1ll*sm*g[i]%ha);
    }


    add(ans,ha-1);
    printf("%d\n",ans);
    return 0;
}
Last modification:July 9th, 2021 at 08:04 am
如果觉得我的文章对你有用,请随意赞赏