2021百度之星初賽 A迷失(DP+flody+二進制優化)


題意:

小 T 迷失在了一個有 n 個點的群島上。
初始時他在 1 號島,他要通過架在島間的 m 座雙向橋,在正好過 k 座橋時達到 n 號島的大門。
這些橋中有若干座附魔橋。當小 T 經過一座附魔橋時,如果他身上沒有附魔標記則被標記,如果他身上已有附魔標記則標記消失。

大門只會在他身上有附魔標記時才會開啟,只有這樣他才能逃離。

小 T 迷失在了群島之間,他每次會等概率隨機挑選一座與他所在島嶼相連的橋走。小 T 向你詢問他能逃離的概率。

保證圖無自環無重邊。

分析:

\(A [ s ] [ i ] [ j ] [ w ]\)表示從\(i\)島恰好經過 \(2^s\)條邊到達 \(j\)島且狀態為 \(w\)時的概率。
題目中 \(k≤10^6\),所以 \(s\)最大不超過20,從低到高枚舉 \(s\),並利用Floyd更新 \(A\)即可。最后從\(A\)中湊出 \(k\)條邊的值即可。
用背包二進制優化即可將步數優化掉\(k=1e^6\)然后每次維護是否是附魔狀態。
或者直接利用快速冪來更新 \(A\)的同時,求出答案。

ll qpow(ll a, ll b)
{
    ll ans = 1;
    while(b)
    {
        if(b & 1) ans = ans * a % mod;
        b >>= 1;
        a = a * a % mod;
    }
    return ans;
}

int n;
ll A[22][110][110][2];
ll g[110][110][2];
ll tmp[110][110][2];
int e[110][110];
void update(int f, int s) ///背包二進制優化
{
    for(int k = 1; k <= n; k++)
    {
        for(int i = 1; i <= n; i++)
        {
            for(int j = 1; j <= n; j++)
            {
                A[s][i][j][0] += A[f][i][k][0] * A[f][k][j][0] % mod; ///2^f步都沒有附魔
                A[s][i][j][0] += A[f][i][k][1] * A[f][k][j][1] % mod; ///都附魔了
                A[s][i][j][1] += A[f][i][k][0] * A[f][k][j][1] % mod;
                A[s][i][j][1] += A[f][i][k][1] * A[f][k][j][0] % mod;
                A[s][i][j][0] %= mod; //都要取模
                A[s][i][j][1] %= mod;
            }
        }
    }
}
void add(int f)
{
    memset(tmp, 0, sizeof tmp);
    for(int k = 1; k <= n; k++)
    {
        for(int i = 1; i <= n; i++)
        {
            for(int j = 1; j <= n; j++)
            {
                tmp[i][j][0] += g[i][k][0] * A[f][k][j][0] % mod;
                tmp[i][j][0] += g[i][k][1] * A[f][k][j][1] % mod;
                tmp[i][j][1] += g[i][k][1] * A[f][k][j][0] % mod;
                tmp[i][j][1] += g[i][k][0] * A[f][k][j][1] % mod;
                tmp[i][j][0] %= mod;
                tmp[i][j][1] %= mod;
            }
        }
    }
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= n; j++)
        {
            for(int k = 0; k <= 1; k++)
            {
                g[i][j][k] = tmp[i][j][k];
            }
        }
    }
}

void solve()
{
    int m, k;
    scanf("%d%d%d", &n, &m, &k);
    memset(e, -1, sizeof e);
    memset(A, 0, sizeof A);
    memset(g, 0, sizeof g);
    for(int i = 1; i <= m; i++)
    {
        int x, y, z;
        scanf("%d%d%d", &x, &y, &z);
        e[x][y] = z; ///建橋
        e[y][x] = z; //是否是附魔橋
    }
    for(int i = 1; i <= n; i++)
    {
        int sum = 0;
        for(int j = 1; j <= n; j++)
        {
            sum += (e[i][j] >= 0); ///是否存在這座橋
        }
        for(int j = 1; j <= n; j++)
        {
            if(e[i][j] < 0) continue; ///不存在這座橋
            A[0][i][j][e[i][j]] = qpow(sum, mod - 2);
        }
    }

    for(int i = 1; i <= n; i++) ///一步 初始化答案
    {
        for(int j = 1; j <= n; j++)
        {
            g[i][j][0] = A[0][i][j][0];
            g[i][j][1] = A[0][i][j][1];
        }
    }
    for(int i = 1; (1 << i) <= k; i++)
        update(i - 1, i);
    k--;
    for(int i = 20; i >= 0; i--)
    {
        if(k >= (1 << i))
        {
            add(i);
            k -= (1 << i);
        }
    }
    printf("%lld\n", g[1][n][1]);
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM