BZOJ3091 城市旅行

题意:

给定一棵树,每个点有点权。要求实现以下操作:

1、若x和y直接相连,断开x和y之间直接相连的边;

2、若x和y不连通,连接x和y;

3、若x和y连通,将x到y的路径上的每个点的点权+d;

4、若x和y连通,求:在x到y的路径上随机选两个点s和t,求s到t路径上的点权和的期望值。注意s和t与t和s算一种方案。


分析:

既然有Link和Cut那么肯定要用动态树……问题在于怎么维护这个期望。

考虑update操作。假设x的左右儿子的期望都已算出,怎样得出x的期望?

x的期望即左右儿子的期望和加上路径端点分别在左儿子内部和右儿子内部(或者在x上)的期望。

令a[1..n]表示x及其子树代表的序列。令x->v表示x的值,x->e表示x的期望,x->lm表示Sigma{i*a[i]},x->rm表示Sigma{(n-i+1)*a[i]},x->s表示x子树的大小那么有:

x->e 

= x->l->e //端点都在左儿子内

 + x->r->e //端点都在右儿子内

 + (x->l->s + 1) * x->r->rm//一端点在x上或者左儿子内、另一端点在右儿子内时,右儿子整体产生的贡献

 + (x->r->s + 1) * x->l->lm//……………………时,左儿子整体产生的贡献

 + (x->l->s + 1) * (x->r->s + 1) * x->v//x自身的贡献

而x的lm和rm也可以维护,那么期望就可以维护了。


需要注意是,由于每个操作都不保证给定的x和y“合法”(写起来有点麻烦,数据倒是好出了……),所以要判连通性(操作4中x、y不连通时要输出-1……WA了好久才发现)。Cut的时候即使x和y不直接相连,只要连通也会被切断。所以要判断x的前驱是否是y(or vice versa)。


代码:

长度7k-,成了目前写的最长的题……由于加了FastIO所以目前在BZOJ上Rank1……在我早上交这道题的时候这题还是可见的……结果现在成了权限题了……

//BZOJ3091; travel; LCT
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <climits>
#include <cmath>
#include <utility>
#include <set>
#include <map>
#include <queue>
#include <ios>
#include <iomanip>
#include <ctime>
#include <numeric>
#include <functional>
#include <fstream>
#include <string>
#include <vector>
#include <bitset>
#include <cstdarg>
using namespace std;
 
typedef long long ll;
typedef unsigned int uint;
#define pair(x, y) make_pair(x, y)
 
#define SIZE 2000000
 
char buffer[SIZE], *ip = buffer;
 
inline ll read() {
    static uint r;
    r = 0U;
    while (*ip < '0' || *ip > '9') ++ip;
    while (*ip >= '0' && *ip <= '9') r = r * 10U + (uint)(*(ip++) - '0');
    return (ll)r;
}
 
#define N 50000
 
ll sqr[N + 1];
 
class Node {
public:
    Node *l, *r, *f;
    bool rev;
    ll lm, rm;
    //Sigma{ i * a[i] }, Sigma{ (s-i+1) * a[i] }
    ll sum, e, v, d;
    //Sum, Expected value, Value, Delta
    int s;
    //Size
    Node(ll _v) {
        l = r = f = NULL;
        rev = false;
        s = 1, d = 0LL, e = sum = lm = rm = v = _v;
    }
} *p[N + 1];
 
ll sum(ll l, ll r) {
    return (l + r) * (r - l + 1LL) >> 1;
}
 
inline void applyDelta(Node *x, ll d) {
    ll ds = sum(1LL, x->s), ex;
    x->sum += (ll)x->s * d;
    x->lm += ds * d, x->rm += ds * d;
    x->d += d, x->v += d;
    //Expected value
    ex = ds * (ll)(x->s + 1) - sqr[x->s];
    x->e += ex * d;
}
 
inline void push(Node *x) {
    if (x->rev) {
        if (x->l) x->l->rev ^= 1, swap(x->l->lm, x->l->rm);
        if (x->r) x->r->rev ^= 1, swap(x->r->lm, x->r->rm);
        swap(x->l, x->r);
        x->rev = false;
    }
    if (x->d) {
        if (x->l) applyDelta(x->l, x->d);
        if (x->r) applyDelta(x->r, x->d);
        x->d = 0;
    }
}
 
inline void update(Node *x) {
    x->sum = x->v + (x->l ? x->l->sum : 0) + (x->r ? x->r->sum : 0);
    x->s = 1 + (x->l ? x->l->s : 0) + (x->r ? x->r->s : 0);
    x->e = x->v + (x->l ? x->l->e : 0LL) + (x->r ? x->r->e : 0LL);
    x->lm = x->rm = x->v;
    if (x->l && !(x->r)) {
        x->e += x->l->lm;
        x->e += (ll)x->l->s * x->v;
        x->lm += x->l->lm + x->v * (ll)x->l->s;
        x->rm += x->l->rm + x->l->sum;
    } else if (x->r && !(x->l)) {
        x->e += x->r->rm;
        x->e += (ll)x->r->s * x->v;
        x->rm += x->r->rm + x->v * (ll)x->r->s;
        x->lm += x->r->lm + x->r->sum;
    } else if (x->l && x->r) {
        //Expected value
        x->e += (ll)(x->l->s + 1) * x->r->rm;
        x->e += (ll)(x->r->s + 1) * x->l->lm;
        x->e += (ll)(x->l->s + 1) * (x->r->s + 1) * x->v - x->v;
        //Left multiplication
        x->lm += x->l->lm + x->r->lm + (x->v + x->r->sum) * (ll)x->l->s + x->r->sum;
        //Right multiplication
        x->rm += x->l->rm + x->r->rm + (x->v + x->l->sum) * (ll)x->r->s + x->l->sum;
    }
}
 
inline void zig(Node *x) {
    Node *y = x->f;
    Node *z = y->f;
    push(x);
    if (z && z->l == y) z->l = x;
    else if (z && z->r == y) z->r = x;
    if (x->r) x->r->f = y;
    y->l = x->r;
    x->r = y, y->f = x, x->f = z;
    update(y);
}
 
inline void zag(Node *x) {
    Node *y = x->f;
    Node *z = y->f;
    push(x);
    if (z && z->l == y) z->l = x;
    else if (z && z->r == y) z->r = x;
    if (x->l) x->l->f = y;
    y->r = x->l;
    x->l = y, y->f = x, x->f = z;
    update(y);
}
 
inline bool isRoot(Node *x) {
    return !(x->f) || (x->f->l != x && x->f->r != x);
}
 
inline void splay(Node *x) {
    Node *y, *z;
    while (!isRoot(x)) {
        y = x->f;
        z = y->f;
        if (z) push(z);
        push(y);
        if (isRoot(y)) {
            if (y->l == x) zig(x);
            else zag(x);
        } else {
            if (z->l == y) {
                if (y->l == x) zig(y), zig(x);
                else zag(x), zig(x);
            } else {
                if (y->l == x) zig(x), zag(x);
                else zag(y), zag(x);
            }
        }
    }
    push(x);
    update(x);
}
 
inline void expose(Node *x) {
    for (Node *y = NULL; x != NULL; x = x->f) {
        splay(x);
        x->r = y;
        update(y = x);
    }
}
 
inline void makeRoot(Node *x) {
    expose(x);
    splay(x);
    x->rev ^= 1;
    swap(x->lm, x->rm);
}
 
inline bool cut(int _x, int _y) {
    Node *x = p[_x], *y = p[_y];
    expose(y);
    splay(y);
    push(y);
    if (y->l) for (push(y = y->l); y->r; push(y = y->r)) ;
    if (x == y) {
        y = p[_y];
        expose(x);
        splay(y);
        y->f = NULL;
        return true;
    }
    return false;
    /*
    expose(x);
    splay(y);
    if (y->f == x) y->f = NULL;
    else {
        expose(y);
        splay(x);
        if (x->f == y) x->f = NULL;
    }
    */
}
 
inline void link(Node *x, Node *y) {
    makeRoot(x);
    x->f = y;
}
 
inline bool connected(int _x, int _y) {
    Node *x = p[_x], *y = p[_y];
    expose(y);
    for (Node *y = NULL; x != NULL; x = x->f) {
        splay(x);
        if (!(x->f)) break;
        y = x;
    }
    push(x);
    while (x->r) push(x = x->r);
    return x == p[_y];
}
 
struct edge {
    int next, node;
} e[N << 1 | 1];
int head[N + 1], tot = 0;
 
inline void addedge(int a, int b) {
    e[++tot].next = head[a];
    head[a] = tot, e[tot].node = b;
}
 
int n, m, a[N + 1], x, y, d;
bool v[N + 1];
 
void build(int x) {
    v[x] = true;
    for (int i = head[x]; i; i = e[i].next) {
        int node = e[i].node;
        if (v[node]) continue;
        p[node]->f = p[x];
        build(node);
    }
}
 
inline void modify(int _x, int _y, ll d) {
    Node *x = p[_x], *y = p[_y];
    expose(y);
    for (y = NULL; x != NULL; x = x->f) {
        splay(x);
        if (!(x->f)) {
            if (x->r) applyDelta(x->r, d);
            if (y) applyDelta(y, d);
            x->v += d;
        }
        x->r = y;
        update(y = x);
    }
}
 
inline pair <ll, int> query(int _x, int _y) {
    Node *x = p[_x], *y = p[_y];
    expose(y);
    for (y = NULL; x != NULL; x = x->f) {
        splay(x);
        if (!(x->f)) {
            ll e = x->v + (x->r ? x->r->e : 0LL) + (y ? y->e : 0LL);
            int s = 1 + (x->r ? x->r->s : 0) + (y ? y->s : 0);
            if (x->r && !y) {
                e += x->r->rm;
                e += (ll)x->r->s * x->v;
            } else if (y && !(x->r)) {
                e += y->rm;
                e += (ll)y->s * x->v;
            } else if (y && x->r) {
                e += (ll)(x->r->s + 1) * y->rm;
                e += (ll)(y->s + 1) * x->r->rm;
                e += (ll)(x->r->s + 1) * (y->s + 1) * x->v - x->v;
            }
            return pair(e, s);
        }
        x->r = y;
        update(y = x);
    }
}
 
ll gcd(ll a, ll b) {
    return !b ? a : gcd(b, a % b);
}
 
int main() {
#ifdef KANARI
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
#endif
 
    fread(buffer, sizeof(char), sizeof(char) * SIZE, stdin);
 
    n = read(), m = read();
    for (int i = 1; i <= n; ++i) a[i] = read();
    for (int i = 1; i < n; ++i) {
        x = read(), y = read();
        addedge(x, y), addedge(y, x);
    }
    for (int i = 1; i <= n; ++i) p[i] = new Node(a[i]);
    build(1);
     
    for (int i = 1; i <= n; ++i) sqr[i] = sqr[i - 1] + (ll)i * i;
     
    while (m--) {
        d = read(), x = read(), y = read();
        if (d == 1) {
            if (x == y) continue;
            if (!cut(x, y)) cut(y, x);
        } else if (d == 2) {
            if (!connected(x, y))
                link(p[x], p[y]);
        } else if (d == 3) {
            d = read();
            if (connected(x, y))
                modify(x, y, d);
        } else {
            if (!connected(x, y)) {
                printf("-1\n");
                continue;
            }
            pair <ll, int> ans = query(x, y);
            ll e = ans.first, s = (ll)(ans.second + 1) * ans.second >> 1;
            ll g = gcd(e, s);
            printf("%lld/%lld\n", e / g, s / g);
        }
    }
 
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值