zzuli 2631: B(树链剖分)

传送门

题目描述

小p很早就听说,在古老的埃及,想要从斯芬克斯守护的地方经过,需要回答对它的一个问题。小p自命不凡,来到了斯芬克斯面前。斯芬克斯每天都望向远处(很远很远)的森林,看着一棵树,树上时不时会有一些小鸟落下,它每天的乐趣就是,当树上的小鸟的数量变化的时候,统计现在树上从一个树叶到另一个树叶上有多少个小鸟,它费尽心思没有搞懂,恰好小p来了,于是它将问题丢给小p,并说到:“我每天望向对面的森林,每天凌晨的时候,树上没有一只小鸟,每当有小鸟来的时候,我就记录 2 x y z,表示这个时候,从x号树叶到y号树叶的路径上每个树叶都多了z只小鸟,每过一段时间,我还会记录 1 x y,表示我现在想知道此时树上x号叶子到y号叶子的路径上小鸟的总数,那么剩下的就交给你了,你要勇于尝试,要么尝试解答,要么尝试丧命。”

输入

第一行一个数字n,表示树上点的个数(1 <= n <= 100000)
第2~n行,每行两个数字x y表示树上一条边,其中x是y 的父节点。
第n + 1行,一个数字q,表示斯芬克斯记录的总数,每个记录的形式如下:
1 x y
2 x y z
含义如题目所示。

输出

对每一个如下形式的记录:
1 x y
输出一行,表示查询的结果

#include<cstdio>
#include<stdlib.h>
#include<algorithm>
#include<iostream>
#include<string>
#include<cstring>
#include<map>
#include<vector>
#include<queue>
#include<math.h>
#include<climits>
#include<set>
#include<sstream>
#include<time.h>
#include<iomanip>

#define debug(x) cout <<#x<<" = "<<x<<endl
#define debug2(x, y) cout<<#x<<" = "<<x<<", "<<#y<<" = "<<y<<endl
#define gg cout <<"---------------QAQ---------------"<<endl
#define fi first
#define SZ(x) (int)x.size()
#define se second
#define pb push_back
#define MEM(a) memset(a, 0, sizeof(a))
#define inf 1e6
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
#define PI cos(-1)
#define endl "\n"
#define eps 1e-8
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<int, pii> piii;
template<class T> inline void read(T &x){
    x=0; char c=getchar(); int f=1;
    while (!isdigit(c)) {if (c=='-') f=-1; c=getchar();}
    while (isdigit(c)) {x=x*10+c-'0'; c=getchar();} x*=f;
}
const int N = 2e5+10, maxn = 1e6+10;
//void FAST(){ios::sync_with_stdio(false);cin.tie(nullptr); cout.tie(nullptr);}
const ll mod =  1e9+7;

int n, m, k;
vector<int> e[N];
int siz[N], deep[N], fa[N], wson[N];
int dfn[N], top[N], pre[N], cnt = 0;
ll lazy[N<<2], sum[N<<2], ori[N];


void dfs1(int u, int father)
{
    siz[u] = 1;
    for(auto v:e[u]) {
        if(v == father) continue;
        deep[v] = deep[u]+1; ///儿子的深度是父亲的深度加一
        fa[v] = u;  /// v的父节点是u
        dfs1(v, u);  /// 遍历儿子节点
        siz[u] += siz[v];  ///儿子节点贡献的子树大小
        if(siz[v] > siz[wson[u]]) ///发现更重的儿子,更新
            wson[u] = v;
    }
}

void dfs2(int u, int tp) ///u节点,及其所在链的链头tp
{
    dfn[u] = ++cnt; ///当前节点的新编号,也就是dfs序的编号
    pre[cnt] = u;   ///老编号
    top[u] = tp;    ///链头
    if(wson[u])     ///有重儿子,一定先访问重儿子
        dfs2(wson[u], tp); ///访问节点,此时重儿子一定再重链,所以还是tp
    for(auto v:e[u])  ///访问所有的轻儿子
    {
        if(v==wson[u]||v==fa[u])///轻儿子节点不能是重儿子,也不能是父亲节点
            continue;
        dfs2(v, v); ///每一个轻儿子,他的链头都是自己
    }
}

void Push_up(int rt)
{
    sum[rt] = sum[rt<<1]+sum[rt<<1|1];
}
void Push_down(int rt, ll m)
{
    if(lazy[rt])
    {
        lazy[rt<<1] += lazy[rt];
        lazy[rt<<1|1] += lazy[rt];
        sum[rt<<1] += lazy[rt]*(m-(m>>1));
        sum[rt<<1|1] += lazy[rt]*(m>>1);
        lazy[rt] = 0;
    }
}
void build(int l = 1, int r = n, int rt = 1)
{
    if(l == r) {
        sum[rt] = ori[pre[l]];
        return ;
    }
    int m = (l+r)>>1;
    build(lson);
    build(rson);
    Push_up(rt);
}
void update(int L, int R, int c,  int l = 1, int r = n, int rt = 1)
{
    if(L<=l&&R>=r) {
        lazy[rt] += c;
        sum[rt] += (ll)c*(r-l+1);
        return ;
    }
    Push_down(rt, r-l+1);
    int m = (l+r)>>1;
    if(L <= m) update(L, R, c,  lson);
    if(R > m) update(L, R, c, rson);
    Push_up(rt);
}
ll query(int L, int R, int l = 1, int r = n, int rt = 1)
{
    if(L<=l&&R>=r) return sum[rt];
    Push_down(rt, r-l+1);
    ll ans = 0;
    int m = (l+r)>>1;
    if(L<=m) ans += query(L, R, lson);
    if(R > m) ans += query(L, R, rson);
    return ans;
}

void Update_sum(int a, int b, int c)
{
    while(top[a]!=top[b])
    {
        if(deep[top[a]] < deep[top[b]])
            swap(a, b);
        update(dfn[top[a]], dfn[a], c);
        a = fa[top[a]];
    }
    if(dfn[a] > dfn[b]) swap(a, b);
    update(dfn[a], dfn[b], c);
}
ll Query_sum(int a, int b)
{
    ll ans = 0;
    while(top[a]!=top[b]) { ///a和b不在一条重链上
        if(deep[top[a]] < deep[top[b]])  ///默认a是在下面
            swap(a, b);
        ans += query(dfn[top[a]], dfn[a]);
        ///访问这条重链,dfs[top[a]]是链头,dfn[a]是当前节点编号
        a = fa[top[a]];  ///往上面访问
    }
    if(dfn[a] > dfn[b]) swap(a, b); ///保证a比b的深度深
    ans += query(dfn[a], dfn[b]); ///他们已经在一条重链上了
    return ans;
}

void solve()
{
    scanf("%d", &n);
    //for(int i = 1; i <= n; ++i) scanf("%lld", &ori[i]);

    for(int i = 1, v, u; i < n; ++i) {
        scanf("%d%d",&u, &v);
        e[u].pb(v);
        e[v].pb(u);
    }

    dfs1(1, 0);
    dfs2(1, 1);
    //build();
    scanf("%d", &m);
    while(m--)
    {
        int op, a, b, c;
        scanf("%d", &op);
        if(op == 1) {
            scanf("%d%d", &a, &b);
            printf("%lld\n", Query_sum(a, b));
        }
        else {
            scanf("%d%d%d", &a, &b, &c);
            Update_sum(a, b, c);
        }
    }
}
int main(){

//	int _; scanf("%d", &_); while(_--)
//    while(~scanf("%d", &n))
//    while(scanf("%d", &n)&&n)
	solve();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44070289/article/details/106813306
今日推荐