1. 程式人生 > >Bubble Cup 11 - Finals [Online Mirror, Div. 1] D. Interstellar battle (bfs序 + 期望 + 樹狀陣列)

Bubble Cup 11 - Finals [Online Mirror, Div. 1] D. Interstellar battle (bfs序 + 期望 + 樹狀陣列)

題目連結:http://codeforces.com/contest/1045/problem/D

題目大意:有一棵n個結點的樹,編號為 i 的結點都有一個屬性p[i],表示 i 這個結點有p[i]的概率會消失。接下來有q次操作,每次操作會把結點u的p[u]值修改為一個新的值np,在每次修改之後,要你求出在這次修改之後,這個樹的會形成的聯通塊的個數的期望。

題目思路:由於可能會消失若干個點,所以這棵樹就會變成由若干個樹組成的森林,由森林的性質我們可以知道,這個森林中聯通塊的數量為 N_V-N_EN_V表示森林內點的數量,N_E表示森林內邊的數量。

那麼這個森林中聯通塊的數量的期望就為E(N_V-N_E),根據期望的性質得,這個式子可以化為E(N_V)-E(N_E)

對於E(N_V)我們很容易就可以得到。現在考慮要怎麼求E(N_E)

一條邊如果存在的話,那麼這條邊連線的兩個點必然是存在的。那麼E(N_E)=\sum_{u}\sum_{v}p[u]*p[v]*1*vis[u][v]vis[u][v]表示u和v之間是否存在邊,存在時值為1,否則為0.

但如果在修改一個點消失的概率之後,每次都要去維護與這個點相連的每條邊的存在概率的話,複雜度是接受不了的。

現在我們考慮修改一個點的消失的概率之後會影響哪些邊,如果把這棵樹看做一棵有根樹的話,它只會對與他的兒子結點相連的邊有影響以及與他父親結點的邊有影響。由於連向父親結點只會有一條邊,所以我們直接更新就好了。

現在考慮連向兒子結點的那些邊,我們可以考慮維護一個bfs序,根據bfs的性質,一個結點的兒子結點的在bfs序中的編號必然是連續,所以我們就可以直接用區間求和來求出這個結點所有兒子結點存在的概率之和,這樣就可以維護這個結點與其兒子結點相連的邊存在的邊數的期望了。

具體實現看程式碼:

#include <bits/stdc++.h>
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pb push_back
#define MP make_pair
#define lowbit(x) x&-x
#define clr(a) memset(a,0,sizeof(a))
#define _INF(a) memset(a,0x3f,sizeof(a))
#define FIN freopen("in.txt","r",stdin)
#define IOS ios::sync_with_stdio(false)
#define fuck(x) cout<<"["<<#x<<" "<<(x)<<"]"<<endl
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int>pii;
typedef pair<ll, ll>pll;
const int MX = 1e5 + 5;
const int inf = 0x3f3f3f3f;

int n, q;
int in[MX], out[MX], id[MX], bfn;
int fa[MX];
double p[MX];
vector<int>G[MX];
struct BIT {
    double a[MX];
    int n;
    void init(int _n) {
        n = _n;
        for (int i = 0; i <= n; i++) a[i] = 0;
    }
    void add(int x, double d) {
        for (; x <= n; x += lowbit(x)) a[x] += d;
    }
    double sum(int x) {
        double res = 0;
        for (; x; x -= lowbit(x)) res += a[x];
        return res;
    }
} T;
void bfs(int s) {
    queue<int>q;
    q.push(s);
    id[s] = ++bfn;
    while (!q.empty()) {
        int u = q.front(); q.pop();
        in[u] = bfn;
        for (auto v : G[u]) {
            if (v == fa[u]) continue;
            fa[v] = u;
            id[v] = ++bfn;
            q.push(v);
        }
        out[u] = bfn;
    }
}

int main() {
    // FIN;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%lf", &p[i]);
        p[i] = 1.0 - p[i];
    }
    for (int i = 1; i < n; i++) {
        int u, v;
        scanf("%d%d", &u, &v);
        u++, v++;
        G[u].pb(v); G[v].pb(u);
    }
    bfs(1); T.init(n);
    for (int i = 1; i <= n; i++)
        T.add(id[i], p[i]);
    double sum1 = 0, sum2 = 0;
    for (int i = 1; i <= n; i++) {
        sum1 += p[i];
        double s = T.sum(out[i]) - T.sum(in[i]);
        sum2 += p[i] * s;
    }
    scanf("%d", &q);
    int u;
    double np;
    while (q--) {
        scanf("%d%lf", &u, &np); u++; np = 1.0 - np;
        sum1 -= p[u]; sum1 += np;
        double s = T.sum(out[u]) - T.sum(in[u]);
        sum2 -= p[u] * s; sum2 += np * s;
        sum2 -= p[u] * p[fa[u]]; sum2 += np * p[fa[u]];
        T.add(id[u], -p[u]); T.add(id[u], np);
        p[u] = np;
        printf("%.7f\n", sum1 - sum2);
    }
    return 0;
}