zoukankan      html  css  js  c++  java
  • 「Luogu4321」随机游走

    「Luogu4321」随机游走

    题目描述
    有一张 (n) 个点 (m) 条边的无向图,(Q) 组询问,每次询问给出一个出发点和一个点集 (S) ,求从出发点出发随机游走走遍这个点集的期望步数。

    (1 leq n leq 18, 1 leq Q leq 10^5)

    解题思路 :

    听说是 ( ext{pkuwc2018d2t3}) 加强版?但是原题时限是1s,各种卡不进去感觉一定要写 ( ext{Min-Max}) 容斥,不过反正我今年听指导建议没报 ( ext{pkuwc})结果 ( ext{thuwc})( ext{py})

    丧心病狂的出题人肯定是要我们 (O(1)) 回答询问了,不过感觉暴力还是蛮好想的,网上以前看到有大佬说过期望要倒着推,然后状压一波就出来了。设 (f(S, u)) 表示当前已经走遍了点集 (S) 接下来走遍全图的期望。这样定义的好处在于终止状态方便表示,直接认为点集外的点已经走过就可以了,如果正着定义走遍点集 (S) 的期望的话,终止状态的计算就需要一堆集合再算上对应的概率了。于是显然有

    [f(S, u) = frac{1}{deg_u} sum_{edge(u, v)} f(S|v, v) + 1 ]

    特别的 (f(S, i) = 0)(S) 是全集 ({1..n}) 的时候。

    这个式子的转移还有环诶,所以还要高斯消元来解,一眼看上去复杂度是 (O((n imes2^n)^3)),复杂度爆炸。

    冷静分析一下,并不是所有的转移都会成环,当满足 (v otin S) 的时候,(S)(S|v) 的真子集,只考虑这样的转移的话转移的形态是一个 ( ext{DAG}) ,更准确的说是一个分层图。

    这启发我们可以来分层求解,把互不相交的环拆开来考虑,而不是一起高斯消元,不妨变换一下转移的式子。

    [f(S, u) = frac{1}{deg_u} sum_{edge(u, v), v otin S} f(S|v, v) + sum_{edge(u, v), v in S} f(S, v) + 1 ]

    此时成环的转移只有后面那个式子,于是我们可以一层一层计算,对于每一个 (S) ,先将前面那个式子的贡献记录在矩阵里面,暴力消元后面那个式子这样每次消元的矩阵大小只有 (n^2) 级别,总复杂度优化到 (O(2^n imes n^3)。)

    另外吐槽一下:我的高斯消元写法好像不加剪枝就被卡常数了,今天做了一天的题没有一道不被卡常,怕是联赛要被卡成暴力分。


    /*program by mangoyang*/
    #pragma GCC optimize("Ofast","inline","-ffast-math")
    #pragma GCC target("avx,sse2,sse3,sse4,mmx")
    #include<bits/stdc++.h>
    #define inf (0x7f7f7f7f)
    #define Max(a, b) ((a) > (b) ? (a) : (b))
    #define Min(a, b) ((a) < (b) ? (a) : (b))
    typedef long long ll;
    using namespace std;
    template <class T>
    inline void read(T &x){
        int ch = 0, f = 0; x = 0;
        for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
        for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
        if(f) x = -x;
    }
    
    const int N = 25, mod = 998244353;
    typedef int Matrix[N][N];
    vector<int> g[N];
    Matrix a; int Ans[(1<<20)+5][N], n, e;
    inline void up(int &x, int y){ (x += y) %= mod; }
    inline void del(int &x, int y){ x = (x + y >= 0) ? x + y : x + y + mod; }
    inline int Pow(int a, int b){
        int ans = 1;
        for(; b; b >>= 1, a = 1ll * a * a % mod)
            if(b & 1) ans = 1ll * ans * a % mod;
        return ans;
    }
    inline void Gauss(Matrix &a){
        int tmp, f;
    	for(int i = 1; i <= n; i++){
            int r = i;
            for(int j = i + 1; j <= n; j++) if(a[j][i] > a[i][i]) r = j;
            for(int j = i; j <= n + 1; j++) swap(a[i][j], a[r][j]);
            if(!a[i][i]) continue;
    		tmp = Pow(a[i][i], mod - 2);
            for(int j = i + 1; j <= n; j++){
                f = 1ll * a[j][i] * tmp % mod;
                for(int k = i; k <= n + 1; k++) 
                    del(a[j][k], 1ll * -a[i][k] * f % mod);
            }
        }
        for(int i = n; i >= 1; i--){	
            for(int j = i + 1; j <= n; j++) 
                del(a[i][n+1], 1ll * -a[j][n+1] * a[i][j] % mod);
            a[i][n+1] = 1ll * a[i][n+1] * Pow(a[i][i], mod - 2) % mod;
        }		
    }
    int main(){
        read(n), read(e);
        for(int i = 1, x, y; i <= e; i++) 
            read(x), read(y), g[x].push_back(y), g[y].push_back(x);
        for(int i = 1; i <= n; i++) Ans[(1<<n)-1][i] = 0;
        for(int s = (1 << n) - 2; s; s--){
            for(int i = 1; i <= n + 1; i++)
                for(int j = 1; j <= n + 1; j++) a[i][j] = 0;
            for(int i = 1; i <= n; i++) if((1 << i - 1) & s){
                int tmp = Pow(g[i].size(), mod - 2);
                for(int j = 0; j < g[i].size(); j++){
                    int v = g[i][j];
                    if(!((1 << v - 1) & s)) up(a[i][n+1], Ans[s|(1<<v-1)][v]);
                    else a[i][v] = (-tmp + mod) % mod; 
                }
                a[i][n+1] = 1ll * tmp * a[i][n+1] % mod;
                a[i][n+1] = (a[i][n+1] + 1) % mod, a[i][i] = 1;
            }
            Gauss(a);
            for(int i = 1; i <= n; i++) Ans[s][i] = a[i][n+1];
        }
        int q; read(q);
        while(q--){
            int num = 0, s, all = (1 << n) - 1; read(num);
            for(int i = 1, x; i <= num; i++) read(x), all ^= (1 << x - 1);
            read(s), all |= (1 << s - 1), printf("%d
    ", Ans[all][s]);
        }
        return 0;
    }
    
  • 相关阅读:
    Windows 下完全卸载 oracle 10g
    WINDOWS SERVER 工作笔记
    白话 WPF/SL 绑定(Binding) (上)
    系统架构整理笔记待续
    在VMware Workstation 中添加硬盘镜像(*.vmdk)
    HTML+CSS 工作笔记
    用Paragon Partition Manager 7.0 给Windows Server 2003 C盘增加空间
    磁盘分区合并增容(WIN 7, XP)
    Oracle 11G Client 客户端安装步骤(图文详解)
    snk
  • 原文地址:https://www.cnblogs.com/mangoyang/p/9913065.html
Copyright © 2011-2022 走看看