loj#6208. 树上询问

20 篇文章 0 订阅
14 篇文章 0 订阅

题面在这里

题意:

给一棵树,三种操作:
1.add(x,d),给x到根路径上的点的ki += d
2.mul(x,d),给x到根路径上的点的ti += ki*d
3.询问一个点的ti值

做法:

树链剖分以后就变成一道线段树好题了qaq
我们考虑在线段树里维护一个tag(a,b,c),ti值即为a*b+c。
这里的a相当于ki。
对于2操作,我们直接b+=d就可以。
对于1操作,我们先给a+=d,然后把多余的减去,即c-=b*d.
然后考虑怎么下传标记。
如果记儿子的tag为tag(a,b,c),父节点的tag为tag(d,e,f),然后我们需要把儿子的tag加上父亲的tag,并且父亲的置为0.
这个时候的ki应该等于a+d,ki的系数等于b+e,常数暂且写c+f,然后肯定有多加的。
我们发现(a+d)*(b+e)=ab+ae+bd+de,而我们需要得到的是a(b+e)+de = ab+ae+de.所以要减去一个bd.
于是我们定义一个新的加法,满足结合律不满足交换律。
于是就解决了。

代码:

/*************************************************************
    Problem: loj 6208 树上询问
    User: bestFy
    Language: C++
    Result: Accepted
    Time: 1203 ms
    Memory: 8708 KiB
    Submit_Time: 2018-01-07 22:48:47
*************************************************************/

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstdlib>
#include<cctype>
#define mid (l+r>>1)
#define lc o<<1
#define rc o<<1|1
using namespace std;
typedef long long LL;

inline LL read()
{
    char ch = getchar(); LL x = 0; int op = 1;
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') op = -1;
    for(; isdigit(ch); ch = getchar()) x = x*10+ch-'0';
    return x*op;
}
inline void write(LL a) { if(a < 0) putchar('-'), a = -a; if(a >= 10) write(a/10); putchar('0'+a%10); }

const int N = 100010;
int n, m, cnt, clk, fa[N], depth[N], sz[N], head[N], top[N], in[N], son[N];
struct Edge{
    int to, nxt;
    Edge() {}
    Edge(int x, int y) { to = x, nxt = y; }
}e[N<<1];
struct tag{
    int a, b, c;
    tag() {};
    tag(int x, int y, int z) { a = x, b = y, c = z; }
    int val() { return a*b+c; }
    void operator += (const tag &other) {
        c += other.c-other.a*b;
        a += other.a; b += other.b;
    }
}sum[N<<2];

inline void addEdge(int x, int y) { e[++ cnt] = Edge(y, head[x]); head[x] = cnt; }
inline void dfs(int u, int lst, int s)
{
    depth[u] = s; fa[u] = lst; sz[u] = 1;
    for(int i = head[u]; i; i = e[i].nxt) {
        int v = e[i].to; if(v == lst) continue;
        dfs(v, u, s+1); sz[u] += sz[v];
        if(!son[u] || sz[v] > sz[son[u]]) son[u] = v;
    }
}
inline void dfs2(int u, int t)
{
    in[u] = ++ clk; top[u] = t;
    if(son[u]) dfs2(son[u], t);
    for(int i = head[u]; i; i = e[i].nxt) if(e[i].to != son[u] && e[i].to != fa[u]) dfs2(e[i].to, e[i].to);
}
inline void pushdown(int o) { sum[lc] += sum[o]; sum[rc] += sum[o]; sum[o] = tag(0, 0, 0); }
inline void add(int o, int l, int r, int x, int y, tag z)
{
    if(l == x && r == y) { sum[o] += z; return; }
    pushdown(o);
    if(y <= mid) add(lc, l, mid, x, y, z);
    else if(x > mid) add(rc, mid+1, r, x, y, z);
    else add(lc, l, mid, x, mid, z), add(rc, mid+1, r, mid+1, y, z);
}
inline void work(int x, int y, int z, int p)
{
    while(top[x] != top[y]) {
        if(depth[top[x]] < depth[top[y]]) swap(x, y);
        add(1, 1, n, in[top[x]], in[x], p?tag(0, z, 0):tag(z, 0, 0));
        x = fa[top[x]];
    }
    if(depth[x] > depth[y]) swap(x, y);
    add(1, 1, n, in[x], in[y], p?tag(0, z, 0):tag(z, 0, 0));
}
inline int query(int o, int l, int r, int x)
{
    if(l == r) return sum[o].val();
    pushdown(o);
    if(x <= mid) return query(lc, l, mid, x);
    else return query(rc, mid+1, r, x);
}
int main()
{
    n = read();
    for(int i = 1; i < n; i ++) {
        int x = read(), y = read(); addEdge(x, y); addEdge(y, x);
    }
    dfs(1, 0, 0); dfs2(1, 1);
    m = read();
    while(m --) {
        int opt = read(), x = read(), y;
        if(opt == 1 || opt == 2) y = read(), work(x, 1, y, opt-1);
        else write(query(1, 1, n, in[x])), puts("");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值