题目: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. 1QT在线帮助文档

    http://www.kuqin.com/qtdocument/classes.html

  2. char[]与TCHAR[]互相转换引发的一个问题!

      软件的一个驱动由于开发的年代比较久一些,使用的是非Unicode编码,而当前新的软件使用的是Unicode编码,于是将非Unicode驱动用于Unicode软件上时,就出现了问题! 问题就出现在非 ...

  3. System.in中的read()方法

    大家先来看例如以下这个程序 public class TestInputStream { public static void main(String args[]) throws IOExcepti ...

  4. Thinkphp中如何书写按照指定字段同步更新的ORM

    群友提出一个问题,如何在更新某个字段的时候同步另一个字段数据过来,即 update table set column1 =column2 where xxx 写原生SQL当然可行,不过既然有ORM那就 ...

  5. 1-1、superset开发环境搭建

    在对superset进行二次开发的过程中,往往需要搭建本地开发环境,修改后立即看到效果,下面我们就讲下开发环境的搭建. 1.打开PyCharm,在菜单栏上执行VCS-->Checkout fro ...

  6. sql中IN的含义

    in后面可以是查询集合,也可以是诸如('1','2','3','4') 例: UPDATE CC_PROBLEM_INFO SET Z_STATUS = 1 WHERE CONTENT_ID IN ( ...

  7. Android之MessageQueue、Looper、Handler与消息循环

    在android的activity中有各种各样的事件,而这些事件最终是转换为消息来处理的.android中的消息系统涉及到: *  消息发送 *  消息队列 *  消息循环 *  消息分发 *  消息 ...

  8. 修复吾爱OD数据窗口双击不出现偏移问题

    吾爱破解专用版OD其实一直有一个小BUG,就是双击数据窗口不出现下图的偏移:<ignore_js_op> 我一直苦苦寻找解决办法,曾见小生怕怕在另一个贴说这是看雪那边OD改了什么东西引起的 ...

  9. spring基础---->spring自定义标签(一)

    Spring具有一个基于架构的扩展机制,可以使用xml文件定义和配置bean.本博客将介绍如何编写自定义XML bean的解析器,并用实例来加以说明.其实我一直相信 等你出现的时候我就知道是你. Sp ...

  10. 【IIS】IIS 7.0/7.5 无法启动 w3svc 服务

    一般情况下,window IIS安装完毕后,会启动C:\inetpub\ 产生 类似C:\inetpub\temp\apppools的文件夹,如果IIS被改动过,此文件夹不会自动生成.需要手动添加. ...