题目:https://www.luogu.org/problemnew/solution/P4211

相当难的一道题,其思想难以用言语表达透彻。

对于每个查询,区间[L,R]中的每个点与z的lca肯定出现在z到根节点的路径上,则路径上的点会对结果产生贡献。那么可以对每个lca向根节点边走边给路径上的每个点贡献+1,求和后的结果就是该查询的答案。当然朴素地做肯定是不行地,可以用线段树维护每个点到根节点上的点贡献之和。

那么每次统计的话,等于将nloglog的复杂度重复q次,也不行。然而每次查询的区间可能会重叠,状态可以转移,考虑离线处理。

对查询根据右端点排序,将一个查询拆成[1,L-1] 和 [1,R]两部分,每次的结果是累计[L,R]中的点到根节点路径上的贡献之和,可以差分处理,所以用[1,R] 的累计之和 - [1, L-1] 的累计之和。

#include<algorithm>
#include<stdio.h>
#include<cstring>
#include<iostream>
#include<vector>
#include<queue>
#include<stack>
#define lson rt<<1
#define rson rt<<1|1
#define Lson l,m,lson
#define Rson m+1,r,rson
using namespace std;
typedef long long LL;
const LL maxn =1e5+;
struct Edge{
LL to,next;
}E[maxn<<];
LL n,head[maxn],tot;
LL idx,size[maxn],fa[maxn],son[maxn],dep[maxn],top[maxn],id[maxn],rnk[maxn];
LL a[maxn];
void init()
{
idx=tot=;
memset(head,-,sizeof(head));
dep[]=,fa[]=,size[]=;
memset(son,,sizeof(son));
}
void AddEdge(LL u,LL v)
{
E[tot] = (Edge){v,head[u]};
head[u]=tot++;
}
void dfs1(LL u)
{
size[u]=;
for(LL i=head[u];~i;i=E[i].next){
LL v=E[i].to;
if(v!=fa[u]){
fa[v]=u;
dep[v]=dep[u]+;
dfs1(v);
size[u]+=size[v];
if(size[son[u]]<size[v]) son[u]=v;
}
}
} void dfs2(LL u,LL topu)
{
top[u]= topu;
id[u] = ++idx;
rnk[idx] = u; //建树用
if(!son[u]) return;
dfs2(son[u],top[u]);
for(LL i=head[u];~i;i=E[i].next){
LL v=E[i].to;
if(v!=fa[u]&&v!=son[u]) dfs2(v,v);
}
} struct STtree{
LL sum,lazy;
}tree[maxn<<]; void pushup(LL rt)
{
tree[rt].sum = tree[lson].sum + tree[rson].sum;
} void pushdown(LL l,LL r,LL rt)
{
if(tree[rt].lazy){
LL add =tree[rt].lazy;
tree[lson].lazy += add;
tree[rson].lazy += add;
LL m = (l+r)>>;
tree[lson].sum += (m-l+)*add;
tree[rson].sum += (r-m)*add;
tree[rt].lazy=;
}
} void update(LL L,LL R,LL v,LL l=,LL r=n,LL rt=)
{
if(L<=l && R>=r){
tree[rt].lazy += v;
tree[rt].sum += (r-l+)*v;
return ;
}
pushdown(l,r,rt);
LL m = (l+r)>>;
if(L<=m) update(L,R,v,Lson);
if(R>m) update(L,R,v,Rson);
pushup(rt);
} LL query(LL L,LL R,LL l=,LL r= n,LL rt=)
{
if(L<=l && R>=r) return tree[rt].sum;
pushdown(l,r,rt);
LL m = (l+r)>>,ans=;
if(L<=m) ans+=query(L,R,Lson);
if(R>m) ans+=query(L,R,Rson);
//pushup(rt);
return ans;
} void UPDATE(LL u,LL v,LL w=)
{
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
update(id[top[u]],id[u],w);
u = fa[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);
update(id[u],id[v],w);
} LL SUM(LL u,LL v)
{
LL ans = ;
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
ans += query(id[top[u]],id[u]);
u = fa[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);
ans += query(id[u],id[v]);
return ans;
} struct Qeury{
LL pos,num,z;
bool tag;
bool operator < (const Qeury & rhs) const {return pos<rhs.pos;}
}que[maxn];
LL res[maxn];
const LL MOD = ; int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
LL q,u,v,tmp;
scanf("%lld %lld",&n,&q);
init();
for(LL i=;i<=n;++i){
scanf("%lld",&u); u++;
AddEdge(u,i);
AddEdge(i,u);
}
dfs1();
dfs2(,);
LL l,r,z,cnt = ;
for(LL i=;i<=q;++i){
scanf("%lld %lld %lld",&l,&r,&z); l++,r++,z++;
que[cnt++] = (Qeury){l-,i,z,};
que[cnt++] = (Qeury){r,i,z,};
}
sort(que,que+cnt);
LL cur =;
for(LL i=;i<cnt;++i){
while(cur<=que[i].pos){
UPDATE(,cur++);
}
if(que[i].tag) res[que[i].num] += SUM(,que[i].z);
else res[que[i].num] -=SUM(,que[i].z);
res[que[i].num] += MOD;
res[que[i].num] %=MOD;
}
for(LL i=;i<=q;++i){
printf("%lld\n",res[i]);
}
return ;
}

洛谷 P4211 [LNOI2014]LCA (树链剖分+离线)的更多相关文章

  1. 洛谷$P4211\ [LNOI2014]\ LCA$ 树链剖分+线段树

    正解:树剖+线段树 解题报告: 传送门$QwQ$ 看到$dep[lca]$啥的就想到之前托腮腮$CSP$模拟$D1T3$的那个套路,,, 然后试下这个想法,于是$dep[lca(x,y)]=\sum_ ...

  2. BZOJ 3626: [LNOI2014]LCA [树链剖分 离线|主席树]

    3626: [LNOI2014]LCA Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2050  Solved: 817[Submit][Status ...

  3. BZOJ 3626: [LNOI2014]LCA( 树链剖分 + 离线 )

    说多了都是泪啊...调了这么久.. 离线可以搞 , 树链剖分就OK了... -------------------------------------------------------------- ...

  4. bzoj3626: [LNOI2014]LCA (树链剖分+离线线段树)

    Description 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1. 设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先. ...

  5. [LNOI2014]LCA 树链剖分 离线 前缀和 思维题

    题目描述:给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1. 设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先. 有q次询问,每 ...

  6. [BZOJ3626] [LNOI2014]LCA(树链剖分)

    [BZOJ3626] [LNOI2014]LCA(树链剖分) 题面 给出一棵N个点的树,要求支持Q次询问,每次询问一个点z与编号为区间[l,r]内的点分别求最近公共祖先得到的最近公共祖先深度和.N, ...

  7. BZOJ2243 洛谷2486 [SDOI2011]染色 树链剖分

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ2243 题目传送门 - 洛谷2486 题意概括 一棵树,共n个节点. 让你支持以下两种操作,共m次操 ...

  8. BZOJ3626[LNOI2014]LCA——树链剖分+线段树

    题目描述 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q次询问,每次询 ...

  9. bzoj 3626 : [LNOI2014]LCA (树链剖分+线段树)

    Description 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q ...

随机推荐

  1. Word中怎么用MathType编辑公式

    现如今,用电脑办公学习已经是一个不可阻挡的潮流.而与之相应的就是需要在这些办公软件进行相应的使用,最典型的一种就是要在Word编辑公式.其实在Word中编辑公式是一个很常见的事情,虽然它很常见,但是却 ...

  2. Boost库初见

    Boost库是一个功能强大.构造精巧.跨平台.开源并且完全免费的C++库,有C++"准"标准库的美称! Boost有着与其它程序库(如MFC等)无法比拟的优点. Boost库采用了 ...

  3. js获取表单数据

    var modelObj = {}; var modelFieldsArray = $('#AddMusicCategory').serializeArray(); $.each(modelField ...

  4. iOS面试题--网络--如何处理多个网络请求的并发的情况

    如何处理多个网络请求的并发的情况 一.概念 1.并发 当有多个线程在操作时,如果系统只有一个CPU,则它根本不可能真正同时进行一个以上的线程,它只能把CPU运行时间划分成若干个时间段,再将时间 段分配 ...

  5. phpredis 中文手册和redis 教程

    phpredis 中文手册  :   http://www.cnblogs.com/zcy_soft/archive/2012/09/21/2697006.html 手册: http://www.cn ...

  6. Codevs 5914 [SXOI2016]最大值

    70分算法+30分打表 #include<ctime> #include<cstdio> #include<cstdlib> #include<algorit ...

  7. LAMP集群项目二 初始化系统

    1.关闭防火墙 /etc/init.d/iptables stop chkconfig iptables off 2.关闭selinux cat /etc/selinux/config #查看状态 s ...

  8. mysql支持跨表删除多条记录

    在Mysql4.0之后,mysql开始支持跨表delete. Mysql可以在一个sql语句中同时删除多表记录,也可以根据多个表之间的关系来删除某一个表中的记录. 假定我们有两张表:Product表和 ...

  9. centos免密登录

    本文是为了docker-machine增加现有虚拟机服务器为节点而做 docker-machine create -d generic --generic-ip-address=192.168.102 ...

  10. spring could Windows打包构建docker镜像到linux

    工程模拟参考:http://blog.csdn.net/forezp/article/details/70198649 一.工程结构 二.Pom配置 <build> <plugins ...