codeforces-div1-282-D

题意见 CF-282-DIV1-D

解法有两种,分为在线和离线,先讲离线的方法。


首先进行一次DFS, 将整棵树表示为欧垃序列,这样对于每个节点,他的子树就在欧垃序列上的一个区间内。

再把问题化为离线,对于每个节点u,将所有需要询问f(u, v)的v点维护在一个vector内。

现在精髓来了,我们再次遍历这棵树,在遍历的过程中维护两个数组,a[]和b[]。

假设我们现在遍历到了点u,则a[v]和b[v]分别维护了dis(u, v)^2, dis(u, v)。

假设u有一个子节点s,当我们准备从u转移到s时,更新a[]和b[]的值。

假设s的子树在欧垃序列上的区间为l到r,于是b[l]~b[r]的值要减去dis(u, s), 而其他b[]的值则要加上dis(u, s)。

对于a[]也可以用于b[]类似的方法来进行维护更新。

于是对于每个询问f(u, v),当我们走到节点u时,假设v代表子树在欧垃序列上的左右边界分别为l和r。

则f(u, v) = sum(a[l], a[l+1], ...a[r]) - (sum(a[1], a[2], ...a[n]) - sum(a[l], a[l+1], ...a[r]))

这就是一个区间求和的操作,于是显然我们可以用一棵线段树来同时维护a[]和b[]。

最终复杂度O(mlogn)


下面是离线方法的代码

#include <stdio.h>
#include <iostream>
#include <string.h>
#include <vector>
#include <math.h>
#include <queue>
#include <map>
#include <set>
#include <algorithm>
using namespace std;

#define FOR(i, j, k) for(int i=(j);i<=(k);i++)
#define REP(i, n) for(int i=0;i<(n);i++)
#define mst(x, y) memset(x, y, sizeof(x));
#define pii pair<int, int>
#define fr first
#define sc second
#define left myleft
#define right myright
#define ll long long
#define ull unsigned long long
#define seed 1331
#define mod ((int)1e9+7)
#define eps 1e-5

int mul(int a, int b){
    long long tmp = (ll)a*(ll)b;
    return ((tmp%mod)+mod)%mod;
}
int sub(int a, int b);
int add(int a, int b){
    if(b < 0)return sub(a, -b);
    return (a+b>mod)?a+b-mod:a+b;
}
int sub(int a, int b){
    if(b < 0)return add(a, -b);
    return (a-b<0)?a-b+mod:a-b;
}

int n, ans[100009], m, timeStramp, idx[100009], init_len[100009];
vector <pii> edge[100009], query[100009];
pii lim[100009];

void dfs0(int u, int fa){
    lim[u].fr = idx[u] = ++timeStramp;
    REP(i, edge[u].size()) if(fa != edge[u][i].fr)
        dfs0(edge[u][i].fr, u);
    lim[u].sc = timeStramp;
}
void dfs1(int u, int fa, int len){
    init_len[idx[u]] = len;
    REP(i, edge[u].size()) if(fa != edge[u][i].fr)
        dfs1(edge[u][i].fr, u, add(len, edge[u][i].sc));
}

struct Node{
    int l, r, sum, sq, flag;
}tree[100009*4];
void update(int u){
    tree[u].sum = add(tree[u*2].sum, tree[u*2+1].sum);
    tree[u].sq = add(tree[u*2].sq, tree[u*2+1].sq);
}
void build(int u, int l, int r){
    tree[u].l=l; tree[u].r=r;
    tree[u].flag = 0;
    if(l == r){
        tree[u].sum = init_len[l];
        tree[u].sq = mul(init_len[l], init_len[l]);
        return ;
    }
    int mid = l+r>>1;
    build(u*2, l, mid);
    build(u*2+1, mid+1, r);
    update(u);
}
void goo(int u, int c){
    int tmp = mul(c, 2);
    tmp = mul(tmp, tree[u].sum);
    tree[u].sq = add(tree[u].sq, tmp);
    tmp = mul(tree[u].r-tree[u].l+1, mul(c, c));
    tree[u].sq = add(tree[u].sq, tmp);
    tree[u].sum = add(tree[u].sum, mul(tree[u].r-tree[u].l+1, c));
    tree[u].flag = add(tree[u].flag, c);
}
void push(int u){
    if(tree[u].flag == 0) return ;
    goo(u*2, tree[u].flag);
    goo(u*2+1, tree[u].flag);
    tree[u].flag = 0;
}
int find_(int u, int l, int r){
    if(tree[u].l==l && tree[u].r==r) return tree[u].sq;
    push(u);
    int mid = tree[u].l+tree[u].r>>1;
    if(r <= mid) return find_(u*2, l, r);
    else if(l > mid) return find_(u*2+1, l, r);
    return add(find_(u*2, l, mid), find_(u*2+1, mid+1, r));
}
void add(int u, int l, int r, int c){
    if(tree[u].l==l&&tree[u].r==r){
        goo(u, c);
        return ;
    }
    push(u);
    int mid = tree[u].l+tree[u].r>>1;
    if(r <= mid) add(u*2, l, r, c);
    else if(l > mid) add(u*2+1, l, r, c);
    else add(u*2, l, mid, c), add(u*2+1, mid+1, r, c);
    update(u);
}

void dfs2(int u, int fa){
    REP(i, query[u].size()){
        int id = query[u][i].fr, v = query[u][i].sc;
        int l = lim[v].fr, r = lim[v].sc;
        int insub = 0, outsub = 0;
        insub = find_(1, l, r);
        if(l != 1) outsub = add(outsub, find_(1, 1, l-1));
        if(r != n) outsub = add(outsub, find_(1, r+1, n));
        ans[id] = sub(insub, outsub);
    }
    REP(i, edge[u].size()){
        int v = edge[u][i].fr, c = edge[u][i].sc;
        if(v == fa) continue;
        int l = lim[v].fr, r = lim[v].sc;

        add(1, l, r, -c);
        if(l != 1) add(1, 1, l-1, c);
        if(r != n) add(1, r+1, n, c);

        dfs2(v, u);

        add(1, l, r, c);
        if(l != 1) add(1, 1, l-1, -c);
        if(r != n) add(1, r+1, n, -c);
    }
}

int main(){
    cin>>n;
    REP(i, n-1){
        int u, v, c;
        cin>>u>>v>>c;
        edge[u].push_back(pii(v, c));
        edge[v].push_back(pii(u, c));
    }
    cin>>m;
    REP(i, m){
        int u, v;
        cin>>u>>v;
        query[u].push_back(pii(i, v));
    }

    timeStramp = 0;
    dfs0(1, 0);
    dfs1(1, 0, 0);
    build(1, 1, n);
    dfs2(1, 0);

    REP(i, m) printf("%d\n", ans[i]);
}





接下来是动态的方法。

个人觉得比离线要复杂一点。

首先进行一次dfs, 算出这三个东西, sons[], subsum[], subsq[], 分别代表每个节点子树的节点数,子树节点到它的距离和,子树节点到它距离的平方的和。

然后再进行一次dfs,这次dfs算出两个东西, totsum[], totsq[], 分别表示其他所有节点到当前节点的距离和,其他所有节点到当前节点的距离的平方的和。

具体的计算方法很简单。

首先对于1号根节点,显然totsum[1]=subsum[1], totsq[1]=subsq[1],

然后从1号节点开始,dfs的过程中用父节点去更新儿子节点,更具体的更新方法不细说,可以打开(x+a)^2+(y+a)^2这个方程想一想。

最后再进行一次dfs, 这次dfs用来初始化倍增算法的LCA,后面会用来算两个节点的祖先和距离。


经过三次dfs后初始化工作算是完成了,

现在依次处理每个询问,

对于每个询问f(u, v), 分两种情况,一种为u为v的子节点,一种为u不是v的子节点,分别考虑。

然后使用之前求出的totsum, totsq, subsum, subsq, sons和dis(u, v){这个可以用LCA求出}

就可以处理得出f(u, v)了。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值