两道树链剖分题目

旧巷老猫 提交于 2020-02-07 20:56:13

2020 CCPC-Wannafly Winter Camp Day2 --- F. 采蘑菇的克拉莉丝

题意:

解法:

考虑暴力解法,枚举起点的所有出边,拿边权乘以子树中蘑菇总数。最坏复杂度\(O(nq)\)
考虑轻重链剖分,每个点只考虑连向父亲的边的贡献,连向重儿子的边的贡献,所有轻儿子的贡献我们用一个tag记录下来。连向父亲的边贡献和连向重儿子的边都可以直接计算。
下面考虑所有轻儿子的贡献。修改点u的蘑菇数量,只可能对他的所有祖先结点的轻链答案有影响(对于一个点v不是u的祖先,那么u对v的贡献一定在指向父亲的边里计算了)。
那么我们从u向上跳重链,对轻链到达的所有点打上贡献标记即可,这样每次修改复杂度\(O(logn)\)
我们通过线段树维护子树内蘑菇大小就可以在\(O(logn)\)的复杂度内求出连向父亲和重儿子的边的贡献。

#include <bits/stdc++.h>
#define ll long long
using namespace std;

const int maxn = 1e6;
int n;
ll bit[maxn + 11];
int son[maxn + 11],siz[maxn + 11],id[maxn + 11],top[maxn + 11],fa[maxn + 11],weight[maxn + 11];
vector <pair<int,int> > edge[maxn + 11];
ll val[maxn + 11];

void dfs(int x,int f) {
    fa[x] = f;
    siz[x] = 1;
    for (auto pi : edge[x]) {
        int v = pi.first;
        if (v == f) continue;
        weight[v] = pi.second;
        dfs(v , x);
        siz[x] += siz[v];
        if (siz[v] > siz[son[x]]) son[x] = v;
    } 
} 

int tot = 0;
void dfs2(int x,int t) {
    top[x] = t;
    id[x] = ++tot;
    if (!son[x]) return;
    dfs2(son[x] , t);
    for (auto pi : edge[x]) {
        int v = pi.first;
        if (v == fa[x] || v == son[x]) continue;
        dfs2(v , v);
    }
}

int lowbit(int x) { return x & (-x); }
void update(int x,int val) { for (; x <= n; x += lowbit(x)) bit[x] += val; }
ll sum(int x) {
    ll ans = 0;
    for ( ; x ; x -= lowbit(x)) ans += bit[x];
        return ans;
}
ll query(int l,int r) { return sum(r) - sum(l - 1); }

ll calc(int v) { 
    ll ans = val[v];
    if (son[v]) ans += query(id[son[v]] , id[son[v]] + siz[son[v]] - 1) * weight[son[v]];
    if (v != 1) ans += (query(1 , n) - query(id[v] , id[v] + siz[v] - 1)) * weight[v];
    return ans;
} 

int main(){
    
    scanf("%d" , &n);
    for (int i = 1; i < n; i++) {
        int u,v,w;
        scanf("%d %d %d",&u,&v,&w);
        edge[u].push_back(make_pair(v , w));
        edge[v].push_back(make_pair(u , w));
    }
    dfs(1 , 0);
    dfs2(1 , 1);
    int q;
    scanf("%d" , &q);
    int s = 1;
    top[1] = 0;
    while (q--) {
        int op;
        scanf("%d" , &op);
        if (op == 1) {
            int v,x;
            scanf("%d %d",&v,&x);
            update(id[v] , x);
            while (v != 1) {
                if (v == top[v]){
                    val[fa[v]] += 1ll * weight[v] * x;
                    v = fa[v];
                }
                else v = top[v];
            }
        } 
        else { 
            int v;
            scanf("%d" , &v);
            s = v;
        } 
        printf("%lld\n" , calc(s));
    } 
} 

Codeforces 1254D Tree Queries

题意:

给定一棵树,每次进行两种操作,第一种操作给一个点v和一个值d,等概率从所有点中选一点r,对于所有u,若u->r的路径经过v,就将u的权值加上d。第二种操作求点v的期望权值。

解法:

对于每个操作一,我们可以推出,对于所有子树v外面的点,当r选在子树v内就可以通过v,所以期望增加\(\frac {d}{n}*siz(v)\);对于所有在子树v内的点,考虑在v的儿子y的子树中,那么
r选在v子树外或者v除了儿子y其他儿子的子树内都可以通过v,所以期望增加\(\frac {d}{n}*(n-siz(y))\)
暴力做法,枚举v的所有儿子,对子树进行修改。
和上面一道题目类似,我们可以进行优化。同样只对重儿子进行修改,轻儿子统一计算。对于每个修改v,我们只会对他的子辈结点期望产生影响。(其他结点都在子树外,可以统一计算)这样我们对v的重儿子所对应的子树进行\(O(logn)\)的线段树暴力修改,再将权值d记录在v结点上。统计答案时,每个结点的答案就是线段树记录的答案加上另外一部分,这部分就是这个结点向上跳遇到的所有轻链连接着的父亲上的tag所产生的答案。跳轻重链复杂度也是\(O(logn)\)

#include <bits/stdc++.h>
#define lson rt << 1
#define rson rt << 1 | 1
#define ll long long
using namespace std;
const ll mol = 998244353;
const int maxn = 150000;
ll tree[4 * maxn + 11],lazy[4 * maxn + 11];
ll tag[maxn + 11];
int siz[maxn + 11],f[maxn + 11],id[maxn + 11],top[maxn + 11],son[maxn + 11];
vector <int> edge[maxn + 11];

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

void dfs(int x,int fa) {
    siz[x] = 1;
    f[x] = fa;
    for (auto v : edge[x]) {
        if (v == fa) continue;
        dfs(v , x);
        siz[x] += siz[v];
        if (siz[v] > siz[son[x]]) son[x] = v;
    }
} 

void dfs2(int x,int t) {
    top[x] = t;
    id[x] = ++tot;
    if (son[x]) dfs2(son[x] , t);
    for (auto v : edge[x]) {
        if (v == f[x] || v == son[x]) continue;
        dfs2(v , v);
    } 
}

ll add(ll a,ll b) { a += b; if (a >= mol) a -= mol; return a; }
ll sub(ll a,ll b) { a -= b; if (a < 0) a += mol; return a; }

void push_up(int rt) { tree[rt] = add(tree[lson] , tree[rson]); }
void push_down(int rt,int l,int r) {
    int mid = (l + r) >> 1;
    ll val = lazy[rt]; lazy[rt] = 0;
    tree[lson] = add(tree[lson] , val * (mid - l + 1) % mol); lazy[lson] = add(lazy[lson] , val);
    tree[rson] = add(tree[rson] , val * (r - mid) % mol); lazy[rson] = add(lazy[rson] , val);
}

void update(int rt,int l,int r,int al,int ar,ll val) {
    if (l > ar || r < al) return;
    if (l >= al && r <= ar) {
        tree[rt] = add(tree[rt] , val * (r - l + 1) % mol);
        lazy[rt] = add(lazy[rt] , val);
        return;
    }
    if (lazy[rt]) push_down(rt , l , r);
    int mid = (l + r) >> 1;
    update(lson , l , mid , al , ar , val);
    update(rson , mid + 1 , r , al , ar , val);
    push_up(rt);
} 

ll query(int rt,int l,int r,int pos) {
    if (l == r) return tree[rt];
    if (lazy[rt]) push_down(rt , l , r);
    int mid = (l + r) >> 1;
    if (mid >= pos) return query(lson , l , mid , pos);
    return query(rson , mid + 1 , r , pos);
}

int main() {
    int n,q;
    scanf("%d %d",&n,&q);
    for (int i = 1; i < n; i++) {
        int u,v;
        scanf("%d %d",&u,&v);
        edge[u].emplace_back(v);
        edge[v].emplace_back(u);
    }
    dfs(1 , 0); 
    dfs2(1 , 1);
    ll all = 0;
    ll inv = qpow(n , mol - 2);
    while (q--) {
        int op,v;
        scanf("%d %d",&op,&v);
        if (op == 1) {
            int d;
            scanf("%d" , &d);
            tag[v] = add(tag[v] , d);
            all = add(all , inv * d % mol * siz[v] % mol);
            if (son[v]) update(1 , 1 , n , id[son[v]] , id[son[v]] + siz[son[v]] - 1 , inv * d % mol * sub(n - siz[v] , siz[son[v]]) % mol);
        }
        else {
            ll ans = add(query(1 , 1 , n , id[v]) , inv * tag[v] % mol * (n - siz[v]) % mol);
            while (v) {
                v = top[v];
                ans = add(ans , tag[f[v]] * inv % mol * sub(n - siz[v] , siz[f[v]]) % mol);
                v = f[v];
            }
            printf("%lld\n" , add(ans , all));
        }
    }
} 
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!