bzoj4012: [HNOI2015]开店

题面在这里

题意:

给一棵树,每个点上有点权,边上有边权。
每次询问一个u,l,r,输出点权在[l,r]范围内的点到u的距离和。
强制在线。

做法:

考虑一棵树上所有点到一个点的距离和怎么求。
不难发现应该等于所有点的深度和+点数×u的深度-2×所有点与u的lca的深度和。
于是问题就在于求所有点与u的lca的深度和。
对于这个问题,我们可以先把每个点到根的路径都打标记,然后查询的时候就是查询u-根的边权×标记数。
可以用树链剖分+线段树实现。

然后题目还有一个要求是点权在[l,r]范围内。
所以就加一个可持久化,把点按照点权从小到大排序,每次的满足要求的点就是区间上连续一段,每种点权上建一棵线段树维护一下即可。

代码:

/*************************************************************
    Problem: bzoj 4012 [HNOI2015]开店
    User: fengyuan
    Language: C++
    Result: Accepted
    Time: 17172 ms
    Memory: 484268 kb
    Submit_Time: 2018-01-08 18:47:16
*************************************************************/

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cctype>
#include<cmath>
#define mid (l+r>>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(a%10+'0'); }

const int N = 150010, M = 20000010;
int n, q, m, cnt, clk, all;
int head[N], num[N], p[N], last[N], in[N], depth[N], sz[N], fa[N], son[N], top[N], rt[N], L[M], R[M];
LL sum[M], times[M], sum_edge[N], sum_dep[N];
struct Node{
    int val, id;
    Node() {}
    Node(int x, int y) { val = x, id = y; }
}a[N];
struct Edge{
    int to, nxt, v;
    Edge() {}
    Edge(int x, int y, int z) { to = x, nxt = y, v = z; }
}e[N<<1];
bool operator < (Node x, Node y) { return x.val < y.val || x.val == y.val && x.id < y.id; }

inline void addEdge(int x, int y, int z) { e[++ cnt] = Edge(y, head[x], z); head[x] = cnt; }
const bool cmp(const Node &x, const Node &y) { return x.val < y.val; }
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+e[i].v); last[v] = e[i].v; 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; sum_edge[clk] = last[u]; top[u] = t;
    if(son[u]) dfs2(son[u], t);
    for(int i = head[u]; i; i = e[i].nxt) if(e[i].to != fa[u] && e[i].to != son[u]) dfs2(e[i].to, e[i].to);
}
inline void add(int pre, int &rt, int l, int r, int x, int y)
{
    rt = ++ all; sum[rt] = sum[pre]; times[rt] = times[pre]; L[rt] = L[pre]; R[rt] = R[pre];
    if(l == x && r == y) { times[rt] ++; return; }
    sum[rt] += sum_edge[y] - sum_edge[x-1];
    if(y <= mid) add(L[pre], L[rt], l, mid, x, y);
    else if(x > mid) add(R[pre], R[rt], mid+1, r, x, y);
    else add(L[pre], L[rt], l, mid, x, mid), add(R[pre], R[rt], mid+1, r, mid+1, y);
}
inline LL query(int rt, int l, int r, int x, int y)
{
    LL ret = 1LL*times[rt]*(sum_edge[y]-sum_edge[x-1]);
    if(l == x && r == y) return ret + sum[rt];
    if(y <= mid) return ret + query(L[rt], l, mid, x, y);
    else if(x > mid) return ret + query(R[rt], mid+1, r, x, y);
    else return ret + query(L[rt], l, mid, x, mid) + query(R[rt], mid+1, r, mid+1, y);
}
inline void modify(int t, int x)
{
    while(top[x] != 1) { add(rt[t], rt[t], 1, n, in[top[x]], in[x]); x = fa[top[x]]; }
    add(rt[t], rt[t], 1, n, 1, in[x]);
}
inline LL getsum(int t, int x)
{
    LL ret = 0;
    while(top[x] != 1) { ret += query(rt[t], 1, n, in[top[x]], in[x]); x = fa[top[x]]; }
    return ret + query(rt[t] , 1, n, 1, in[x]);
}
int main()
{
    n = read(), q = read(), m = read();
    for(int i = 1; i <= n; i ++) a[i].val = read(), a[i].id = i;
    sort(a+1, a+1+n);
    for(int i = 1; i < n; i ++) {
        int x = read(), y = read(), z = read();
        addEdge(x, y, z); addEdge(y, x, z);
    }
    dfs(1, 0, 0); dfs2(1, 1);
    for(int i = 1; i <= n; i ++) { sum_edge[i] += sum_edge[i-1]; sum_dep[i] = sum_dep[i-1]+depth[a[i].id]; }
    for(int i = 1; i <= n; i ++) { rt[i] = rt[i-1]; modify(i, a[i].id); }
    LL lastans = 0;
    while(q --) {
        int x = read(), l = (read()+lastans)%m, r = (read()+lastans)%m; 
        if(l > r) swap(l, r); Node ll, rr; ll = Node(l, 0), rr = Node(r, 1e9);
        l = lower_bound(a+1, a+1+n, ll)-a;
        r = upper_bound(a+1, a+1+n, rr)-a-1;
        lastans = 1LL*(r-l+1)*depth[x] + sum_dep[r]-sum_dep[l-1]-2LL*(getsum(r, x)-getsum(l-1, x));
        write(lastans), puts("");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值