冲刺NOI2017 (20) 距离 (可持久化树链剖分)

题目大意

给定一棵 n 个点的边带权的树,以及一个排列p,有 q 个询问,给定点 l,r,k,希望你求出:

jlrdis(p[j],k)

其中 dis(u,v) 表示在树上 u v的最短路径的长度。

数据范围: (n,p<=2105)


题解

我们先抛开这道题不谈,讨论另一个问题:

.

距离和
  • 对于一棵有 n 个节点带权的树,树上有m个黑点。现在有 q 个询问,对于每个询问应该回答点k到树上所有黑点的距离之和。 (n,m,q<=2105)

分析这个问题,这个问题的答案可以表示为所有的黑点到k点的距离之和,即可以表示为以下形式:

ans=xdis(root,x)+dis(root,k)2dis(root,lca(x,k))=xdis(root,x)+xdis(root,k)2xdis(root,lca(x,k))

因为对于每一个询问都包含了所有的黑点,所以式子的第一部分可以在每次向树中添加黑点的时候,树剖查询黑点到根节点的距离,并累加统计起来预处理好。

对于式子的第二部分,因为只与 k 点到根节点的距离和黑点的个数有关,所以可以O(nlogn2)查询k点到根节点的距离,然后乘上黑点的个数就可以。

对于式子的第三部分,我们想要统计的是 lca(k,x) 到根节点的距离,因为两点的 lca 到根节点的路径相当于两个点到根节点的路径的公共部分。因此,我们再维护另一个树剖,每在图中加入一个黑点,就在新树剖上的这个黑点到根节点的路径上的每一条边加上这条边的原边权,形如这个黑点从自己的位置走到了根节点并留下了自己的痕迹——将这条路径上每条边的权值都加上一倍的原树的边权,每留下一个痕迹就表示有一个黑点要路过这条边一次。
那么,把所有的黑点加入后,每对于一个 k 点进行一次查询,我们所求的这部分式子的值就等于在新维护的这个树剖上(彩色)k点到根节点的权值和,因为两点的 lca 到根节点的路径相当于两个点到根节点的路径的公共部分



所以每对一个k点进行询问,用以上的方法对答案式子的三部分进行统计相加即可,时间复杂度 O(nlogn2) 如果对于这个问题看到这里,还是感到不是很懂的话,请从头再看一遍,因为这个问题是解决这道题的前置技能。

.

回到正题

掌握了上述的前置技能之后,我们就可以运用一些奇技淫巧来解决这道题了。为了方便描述,我们把题目输入中给出的原树称作树1,原树关于 p[] 序列的映射的树称作树2。

因为询问是针对树1的询问,所以我们应该从树1下手分析。每当我们在树1中选用一个点 i ,就相当于在树2中加入了一个黑点p[i],每加入一个黑点就相当于在树2对应的树剖上进行几次操作。但是显然对于这个问题来说,每次都一一将树1路径中的点对应的黑点加入的话,复杂度一定是爆炸的。

由于每一个点的加入都相当于对树剖线段树进行多次修改,所以不能用树剖来维护,而每次查询都需要支持查询多个版本,考虑使用主席树来维护各个版本。

我们从根节点开始向下逐个插入,每在树1中以dfs序插入一个点,就相当于在树2中加入一个黑点,同时也相当于在其父亲的线段树版本上进行了一次修改,这个部分用主席树维护版本即可。每一个版本就相当于一个黑点的集合。

那么对于一个 l,r,k ,只要用 l root的点集加 r root的点集减去 lca root 的点集减去 lca 的父节点到 root 的点集统计出答案就可以了。


代码

#include <cstdio>
#include <iostream>
//#include <ctime>
#include <algorithm>
using namespace std;

const int STRSIZE=40000000;
char in1[STRSIZE];
char *in=in1, *tempend;

void Input() {
    tempend=in+STRSIZE;
    fread(in,1,STRSIZE,stdin);
}
inline void scan(int &x) {
    char c=*(in++);
    while(!(c>='0' && c<='9')) c=*(in++);
    x=c-'0';
    c=*(in++);
    while(c>='0' && c<='9') {
        x=x*10+c-'0';
        c=*(in++);
    }
}
inline void scan(long long &x) {
    char c=*(in++);
    while(!(c>='0' && c<='9')) c=*(in++);
    x=c-'0';
    c=*(in++);
    while(c>='0' && c<='9') {
        x=x*10+c-'0';
        c=*(in++);
    }
}

const int maxn=int(2e5)+111;
int type,n,q;
int p[maxn];
int head[maxn], TOT=0;

struct Edge {
    int to,cost,next;
    Edge() {}
    Edge(const int &y,const int &co,const int &nx):to(y),cost(co),next(nx) {}
}eage[maxn<<1];

inline void Add_eage(const int &x,const int &y,const int &cost) {
    eage[TOT]=Edge(y,cost,head[x]), head[x]=TOT++;
    eage[TOT]=Edge(x,cost,head[y]), head[y]=TOT++;
}

void Read() {
    scan(type), scan(n), scan(q);
    register int i,x,y,z;
    for(i=1;i<=n;++i) head[i]=-1;
    for(i=2;i<=n;++i) {
        scan(x), scan(y), scan(z);
        Add_eage(x,y,z);
    }
    for(i=1;i<=n;++i)
        scan(p[i]);
    return;
}

int dep[maxn], siz[maxn], fa[maxn], son[maxn], pw[maxn];
void dfs1(const int &u) {
    register int i,v;
    siz[u]=1, son[u]=0;
    for(i=head[u];~i;i=eage[i].next) if(eage[i].to!=fa[u]) {
        v=eage[i].to;
        fa[v]=u, dep[v]=dep[u]+1, pw[v]=eage[i].cost;
        dfs1(v);
        siz[u]+=siz[v];
        if(siz[v]>siz[son[u]]) son[u]=v;
    }
}

int top[maxn], seq[maxn], id[maxn], ind;
void dfs2(const int &u) {
    seq[id[u]=++ind]=u;
    if(u==son[fa[u]]) top[u]=top[fa[u]];
    else top[u]=u;
    if(son[u]) dfs2(son[u]);
    register int i;
    for(i=head[u];~i;i=eage[i].next) if(eage[i].to!=fa[u] && eage[i].to!=son[u])
        dfs2(eage[i].to);
    return;
}

#define ls(k) ((k)<<1)
#define rs(k) (ls(k)|1)
#define mid ((l+r)>>1)

int val[maxn];
long long org[maxn<<2];

void org_Build(const int &k,const int &l,const int &r) {
    org[k]=0;
    if(l==r) {
        org[k]=val[l];
        return;
    }
    org_Build(ls(k),l,mid);
    org_Build(rs(k),mid+1,r);
    org[k]=org[ls(k)]+org[rs(k)];
    return;
}

long long org_Query(int k,int l,int r,int ql,int qr) {
    if(ql<=l && r<=qr) return org[k];
    long long tmp=0;
    if(ql<=mid) tmp+=org_Query(ls(k),l,mid,ql,qr);
    if(qr> mid) tmp+=org_Query(rs(k),mid+1,r,ql,qr);
    return tmp;
}

long long orgtree_Query(register int u,register int v) {
    long long tmp=0;
    while(top[u]!=top[v]) {
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        tmp+=org_Query(1,1,n,id[top[u]],id[u]);
        u=fa[top[u]];
    }
    if(dep[u]>dep[v]) swap(u,v);
    tmp+=org_Query(1,1,n,id[u],id[v]);
    return tmp;
}

int LCA(int u,int v) {
    while(top[u]!=top[v]) {
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        u=fa[top[u]];
    }
    if(dep[u]>dep[v]) swap(u,v);
    return u;
}

void Spilt() {
    fa[1]=0, dep[1]=1;
    dfs1(1);
    dfs2(1);
    register int i;
    for(i=1;i<=n;++i) val[i]=pw[seq[i]];
    org_Build(1,1,n);
    return;
}

struct Node {
    long long res;
    int add,ls,rs;
}node[int(1e7)];

int root[maxn];
int tot=0;

void Build(int &k,int l,int r) {
    k=++tot;
    node[k].res=node[k].add=node[k].ls=node[k].rs=0;
    if(l==r) return;
    Build(node[k].ls,l,mid);
    Build(node[k].rs,mid+1,r);
    return;
}

long long Modify(int &k,int ok,int pre,int l,int r,int ql,int qr) {
    if(!k) node[k=++tot]=node[pre];
    long long cur=0;
    if(ql==l && r==qr) {
        ++node[k].add;
        node[k].res+=org[ok];
        return org[ok];
    }
    if(qr<=mid) {
        if(node[k].ls<k) node[k].ls=0;
        cur+=Modify(node[k].ls,ls(ok),node[pre].ls,l,mid,ql,qr);
    } else if(ql>mid) {
        if(node[k].rs<k) node[k].rs=0;
        cur+=Modify(node[k].rs,rs(ok),node[pre].rs,mid+1,r,ql,qr);
    } else {
        if(node[k].ls<k) node[k].ls=0;
        cur+=Modify(node[k].ls,ls(ok),node[pre].ls,l,mid,ql,mid);
        if(node[k].rs<k) node[k].rs=0;
        cur+=Modify(node[k].rs,rs(ok),node[pre].rs,mid+1,r,mid+1,qr);
    }
    node[k].res+=cur;
    return cur;
}

void tree_Modify(int x,int y,int u,int v) {
    while(top[u]!=top[v]) {
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        Modify(root[x],1,root[y],1,n,id[top[u]],id[u]);
        u=fa[top[u]];
    }
    if(dep[u]>dep[v]) swap(u,v);
    Modify(root[x],1,root[y],1,n,id[u],id[v]);
    return;
}

#define mp make_pair
typedef pair<long long,long long> pll;

pll Query(int k,int ok,int l,int r,int ql,int qr) {
    if(ql==l && r==qr) return mp(node[k].res,org[ok]);
    long long tmp1=0, tmp2=0;
    pll tmp;
    if(qr<=mid) {
        tmp=Query(node[k].ls,ls(ok),l,mid,ql,qr);
        tmp1+=tmp.first, tmp2+=tmp.second;
    } else if(ql>mid) {
        tmp=Query(node[k].rs,rs(ok),mid+1,r,ql,qr);
        tmp1+=tmp.first, tmp2+=tmp.second;
    } else {
        tmp=Query(node[k].ls,ls(ok),l,mid,ql,mid);
        tmp1+=tmp.first, tmp2+=tmp.second;
        tmp=Query(node[k].rs,rs(ok),mid+1,r,mid+1,qr);
        tmp1+=tmp.first, tmp2+=tmp.second;
    }
    return mp(tmp1+tmp2*node[k].add,tmp2);
}

long long tree_Query(int x,int u,int v) {
    long long tmp=0;
    while(top[u]!=top[v]) {
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        tmp+=Query(root[x],1,1,n,id[top[u]],id[u]).first;
        u=fa[top[u]];
    }
    if(dep[u]>dep[v]) swap(u,v);
    tmp+=Query(root[x],1,1,n,id[u],id[v]).first;
    return tmp;
}

void dfs(int u) {
    register int i,v;
    for(i=head[u];~i;i=eage[i].next) if(eage[i].to!=fa[u]) {
        v=eage[i].to;
        tree_Modify(v,u,1,p[v]);
        dfs(v);
    }
} 

void Init() {
    Build(root[0],1,n);
    tree_Modify(1,0,1,p[1]);
    dfs(1);
}

inline long long Getres(const int &x,const int &k) {
    return orgtree_Query(1,k)*dep[x]+Query(root[x],1,1,n,1,n).first-(tree_Query(x,1,k)<<1);
}
inline long long Getans(const int &l,const int &r,const int &k) {
    int lca=LCA(l,r);
    long long tmp=Getres(l,k)+Getres(r,k)-Getres(lca,k);
    if(fa[lca]) tmp-=Getres(fa[lca],k);
    return tmp;
}

void Solve() {
    register int i;
    long long l,r,k,last=0;
    for(i=1;i<=q;++i) {
        scan(l), scan(r), scan(k);
        l^=(type*last), r^=(type*last), k^=(type*last);
        printf("%lld\n",last=Getans(l,r,k));
    }
    return;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("dis.in","r",stdin);
    freopen("dis.out","w",stdout);
#endif
//  double t1=clock();

    Input();
    Read();
    Spilt();
    Init();
    Solve();

//  printf("%.3lfsec\n",(clock()-t1)/CLOCKS_PER_SEC);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值