題解-CSA Round#18 Randomly Permuted Costs

Problem

CSA Round 18c++

題意概要:給定一個有重邊有自環 \(n\)\(m\) 邊的有向無環圖(DAG),每條邊有其權值,每當你走到一個點 \(x\) 時,全部從 \(x\) 連出去的邊上的權值會互相隨機打亂,問從 \(S\)\(T\) 最短路長度的指望dom

\(n,m\leq 10^3\)優化

Solution

首先第一步很明顯是按照 DAG 的拓撲序一個個地轉移,只需考慮處理每一個點怎麼轉移,設 \(f[x]\) 表示從 \(x\) 走到 \(T\) 的最短路長度指望spa


先暫不考慮重邊和自環code

設當前在點 \(x\),且共有 \(K\) 條出邊,那麼 \(f[x]\) 應該會有 \(K^2\) 種取值(\(K\) 種出邊邊權和 \(K\)\(f[v](x\rightarrow v)\) 值自由搭配)排序

考慮計算若當前選擇了 邊權 \(w\) 與 出點\(f[v](x\rightarrow v)\) 搭配,須要計算 \(w+f[v]\) 做爲全部 \(K\) 種搭配下最小的權值的機率ci

對於 \(x\) 的每個不一樣於 \(v\) 的出點 \(t\),須要計算出有多少邊權與其搭配使得這組搭配花費總和不小於 \(w\)\(v\) 的這組搭配,記做 \(ret[t]\)get

一個結論是 \(ret[t]\)\(f[t]\) 正相關(由於若 \(f[t]\) 越大,則給邊權 \(t_w\) 的限制越小,\(f[t]+t_w\geq w+f[v]\Leftrightarrow t_w\geq w+f[v]-f[t]\))。而對於 \(f[i]\leq f[j]\),定有 \(ret[i] \leq ret[j]\),且二者集合之間爲包含關係(後者包含前者)it

則考慮將全部出點按照 \(f\) 值從小至大排序後,\(t\) 處在第 \(i\) 位,則減去其包含的區間,這一位能選的配對有 \(ret[t]-(i-1)\)io

因此邊權 \(w\) 與出點 \(f[v]\) 的配對爲 \(K\) 個配對最小的狀況,有 \(\prod_t (ret[t]-i+1)\) 種,這樣就能在 \(O(K^3)\) 的時間內計算出每一個點的 \(f\) 值;事實上,按照配對的權值排序,計算增量出現的機率,每一次只會改變兩個值,能夠 \(O(1)\) 解決,算上排序,能夠在 \(O(K^2\log K)\) 的時間內計算出每一個點的 \(f\)


考慮上重邊,發現 \(x\) 如有 \(k\) 條邊指向 \(v\),則只要假定 \(x\)\(k\)\(f\) 值爲 \(f[v]\) 的出點便可


考慮上自環,可使用二分 \(f\) 的方式,每次假定 \(f=mid\),和由此算出來的 \(f'\) 進行對比,若 \(f<f'\) 則表明 \(mid\) 設得比較小,反之則越大(事實上二分中也能夠用一點點小優化,若 \(f<f'\) 則將 \(l\) 設爲 \(f'\) 而非 \(mid\),這樣實際上算一種小迭代?速度上升一倍)

加上二分後就須要將對配對的排序提到外頭來,內層僅處理自環的部分後線性歸併一下,不然複雜度會變成兩個 \(\log\)


時間複雜度爲 \(O(n\log p+m^2\log m)\)\(p\) 視精度要求而定)

Code

#include <bits/stdc++.h>
using namespace std;
#define For(x,y) for(int x=1;x<=y;++x)

const double eps = 1e-8;
const int N = 1010;
struct Edge {int v, nxt; double w;} a[N+N];
int head[N], Head[N], _;
int deg[N], q[N];
double f[N];
int n, m, S, T;

inline void ad() {
    static int x, y; static double w; scanf("%d%d%lf",&x,&y,&w);
    a[++_].v = y, a[_].w = w, a[_].nxt = head[x], head[x] = _;
    a[++_].v = x, a[_].nxt = Head[y], Head[y] = _;
    if(x != y) ++deg[x];
}

typedef pair<double,int> pr;
pr brr[N*N], arr[N*N], crr[N*N];
int arc, brc, crc;

double ew[N*N]; int _ew;
double ot[N*N]; int _ot;
int ret[N], self_circle;

void init(int x) {
    arc = _ew = _ot = self_circle = 0;
    for(int i=head[x];i;i=a[i].nxt) {
        ew[++_ew] = a[i].w;
        if(a[i].v == x) ++self_circle;
        else ot[++_ot] = f[a[i].v];
    }
    sort(ew + 1, ew + _ew + 1);
    sort(ot + 1, ot + _ot + 1);
    For(i, _ew) For(j, _ot)
        arr[++arc] = make_pair(ew[i] + ot[j], j);
    sort(arr + 1, arr + arc + 1);
}

double calc(int x, double sw) {
    f[x] = sw, brc = 0;
    int t = 0; while(t < _ot and ot[t+1] < sw) ++t;
    For(i, _ew) For(j, self_circle)
        brr[++brc] = make_pair(ew[i] + sw, t + j);
    For(i, arc) if(arr[i].second > t) arr[i].second += self_circle;
    merge(arr+1, arr+arc+1, brr+1, brr+brc+1, crr+1);
    For(i, arc) if(arr[i].second > t) arr[i].second -= self_circle;
    
    crc = arc + brc;
    
    For(i, _ew) ret[i] = _ew;
    double coe = 1, ans = 0;
    for(int i=1, id; i <= crc and coe > eps; ++i) {
        ans += coe * (crr[i].first - crr[i-1].first);
        id = crr[i].second;
        coe /= ret[id] - (id - 1);
        --ret[id];
        coe *= ret[id] - (id - 1);
    }
    return ans;
}

void solve(int x) {
    init(x);
    if(!_ot) return f[x] = 1e9, void();
    double l = calc(x, 0), r = calc(x, 1e7), mid, res;
    while(l + eps < r) {
        mid = 0.5 * (l + r);
        res = calc(x, mid);
        if(res > mid) l = res;
        else r = res;
    }
    f[x] = l;
}

int main() {
    scanf("%d%d%d%d",&n,&m,&S,&T);
    while(m--) ad();
    
    int he = 1, ta = 0;
    For(i, n) {
        if(!deg[i]) q[++ta] = i;
        f[i] = 1e9;
    }
    while(he <= ta) {
        int x = q[he++];
        for(int i=Head[x];i;i=a[i].nxt)
            if(!(--deg[a[i].v]))
                q[++ta] = a[i].v;
        if(x == T) f[x] = 0;
        else solve(x);
    }
    if(f[S] < 1e8) printf("%.7lf\n",f[S]);
    else puts("-1");
    return 0;
}
相關文章
相關標籤/搜索