BZOJ 3626 [LNOI2014]LCA ——树链剖分
思路转化很巧妙。
首先把询问做差分。
然后发现加入一个点就把路径上的点都+1,询问的时候直接询问到根的路径和。
这样和原问题是等价的,然后树链剖分+线段树就可以做了。
#include <map>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define F(i,j,k) for (int i=j;i<=k;++i)
#define D(i,j,k) for (int i=j;i>=k;--i)
#define ll long long
#define mp make_pair
#define md 201314
#define maxn 100005 int n,q,dep[maxn],data[maxn],ans[maxn]; namespace SegTree{
int sum[maxn<<3],dsum[maxn<<3],tag[maxn<<3];
void update(int o)
{
sum[o]=(sum[o<<1]+sum[o<<1|1])%md;
}
void build(int o,int l,int r)
{
if (l==r)
{
sum[o]=tag[o]=0;
return ;
}
int mid=l+r>>1;
build(o<<1,l,mid); build(o<<1|1,mid+1,r);
update(o);
}
void pushdown(int o,int l,int r)
{
if (tag[o]!=0)
{
int mid=l+r>>1;
tag[o<<1]+=tag[o];tag[o<<1|1]+=tag[o];
sum[o<<1]+=tag[o]*(mid-l+1);
sum[o<<1|1]+=tag[o]*(r-mid);
tag[o]=0;
}
}
int querysum(int o,int l,int r,int L,int R)
{
if (L<=l&&r<=R) return sum[o];
pushdown(o,l,r);
int mid=l+r>>1;
if (R<=mid) return querysum(o<<1,l,mid,L,R);
else if (L>mid) return querysum(o<<1|1,mid+1,r,L,R);
else return (querysum(o<<1,l,mid,L,R)+querysum(o<<1|1,mid+1,r,L,R))%md;
}
void modify(int o,int l,int r,int L,int R,int f)
{
if (L<=l&&r<=R)
{
sum[o]+=(r-l+1)*f;
tag[o]+=f;
return ;
}
pushdown(o,l,r);
int mid=l+r>>1;
if (R<=mid) return modify(o<<1,l,mid,L,R,f),update(o);
else if (L>mid) return modify(o<<1|1,mid+1,r,L,R,f),update(o);
else return modify(o<<1,l,mid,L,R,f),modify(o<<1|1,mid+1,r,L,R,f),update(o);
}
} namespace Tree{
int h[maxn],to[maxn],ne[maxn],en=0;
int siz[maxn],son[maxn],dfn[maxn],top[maxn],fa[maxn],tot;
int pos[maxn],id[maxn];
vector < pair<int,int> > v[maxn];
void add(int a,int b)
{
to[en]=b;ne[en]=h[a];h[a]=en++;
}
void dfs1(int o)
{
siz[o]=1;
for (int i=h[o];i>=0;i=ne[i])
{
dep[to[i]]=dep[o]+1;
fa[to[i]]=o;
dfs1(to[i]);
siz[o]+=siz[to[i]];
if (siz[to[i]]>siz[son[o]]) son[o]=to[i];
}
}
void dfs2(int o,int tp)
{
top[o]=tp;pos[o]=++tot;id[tot]=o;
if (!son[o]) return;
dfs2(son[o],tp);
for (int i=h[o];i>=0;i=ne[i])
if (to[i]!=son[o]) dfs2(to[i],to[i]);
return ;
}
void build()
{
F(i,1,n) data[i]=dep[id[i]];
SegTree::build(1,1,n);
}
void add(int a,int b,int f)
{
while (top[a]!=top[b])
{
if (dep[top[a]]<dep[top[b]]) swap(a,b);
SegTree::modify(1,1,n,pos[top[a]],pos[a],f);
a=fa[top[a]];
}
if (dep[a]<dep[b]) swap(a,b);
SegTree::modify(1,1,n,pos[b],pos[a],f);
}
int query(int a,int b)
{
int ret=0;
while (top[a]!=top[b])
{
if (dep[top[a]]<dep[top[b]]) swap(a,b);
ret+=SegTree::querysum(1,1,n,pos[top[a]],pos[a]);
ret%=md;
a=fa[top[a]];
}
if (dep[a]<dep[b]) swap(a,b);
ret+=SegTree::querysum(1,1,n,pos[b],pos[a]);
return ret%md;
}
void work()
{
F(i,1,q)
{
int l,r,z; scanf("%d%d%d",&l,&r,&z);l++;r++;z++;
v[l-1].push_back(mp(z,-i));
v[r].push_back(mp(z,i));
}
F(i,1,n)
{
add(1,i,1);
for (int j=0;j<v[i].size();++j)
{
pair<int,int> pa=v[i][j];
if (pa.second<0) ans[-pa.second]-=query(1,pa.first);
else ans[pa.second]+=query(1,pa.first);
}
}
F(i,1,q) printf("%d\n",(ans[i]+md)%md);
}
} namespace Graph{
int h[maxn],to[maxn],ne[maxn],en=0;
void add(int a,int b)
{to[en]=b;ne[en]=h[a];h[a]=en++;}
void dfs(int o,int fa)
{
if (fa) Tree::add(fa,o);
for (int i=h[o];i>=0;i=ne[i])
if (to[i]!=fa) dfs(to[i],o);
}
} int main()
{
scanf("%d%d",&n,&q);
memset(Tree::h,-1,sizeof Tree::h);
memset(Graph::h,-1,sizeof Graph::h);
F(i,2,n)
{
int fa; scanf("%d",&fa);
Graph::add(fa+1,i);
}
Graph::dfs(1,0);
dep[1]=1;
Tree::dfs1(1);
Tree::dfs2(1,1);
Tree::build();
Tree::work();
}
BZOJ 3626 [LNOI2014]LCA ——树链剖分的更多相关文章
- BZOJ 3626: [LNOI2014]LCA 树链剖分 线段树 离线
http://www.lydsy.com/JudgeOnline/problem.php?id=3626 LNOI的树链剖分题没有HAOI那么水,学到的东西还是很多的. 我如果现场写,很难想出来这种题 ...
- BZOJ 3626: [LNOI2014]LCA [树链剖分 离线|主席树]
3626: [LNOI2014]LCA Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 2050 Solved: 817[Submit][Status ...
- BZOJ 3626: [LNOI2014]LCA( 树链剖分 + 离线 )
说多了都是泪啊...调了这么久.. 离线可以搞 , 树链剖分就OK了... -------------------------------------------------------------- ...
- bzoj 3626 : [LNOI2014]LCA (树链剖分+线段树)
Description 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q ...
- [BZOJ3626] [LNOI2014]LCA(树链剖分)
[BZOJ3626] [LNOI2014]LCA(树链剖分) 题面 给出一棵N个点的树,要求支持Q次询问,每次询问一个点z与编号为区间[l,r]内的点分别求最近公共祖先得到的最近公共祖先深度和.N, ...
- BZOJ3626[LNOI2014]LCA——树链剖分+线段树
题目描述 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q次询问,每次询 ...
- 【bzoj3626】[LNOI2014]LCA 树链剖分+线段树
题目描述 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q次询问,每次询 ...
- bzoj3626 [LNOI2014]LCA——树链剖分
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3626 思路很巧妙,把区间换成前缀和相减: 把 l ~ r 到根路径上的点的点权都+1,然后 ...
- [LNOI2014]LCA(树链剖分)
BZOJ传送门 Luogu传送门 题目:给你一棵树,给你n个询问,每个询问要求输出$\sum_{i=l}^{r}depth(LCA(i,z))$ 细看看其实没有想象的那么难 大体思路: 1.对于每个询 ...
随机推荐
- Android(java)学习笔记109:Java中输入和输出流概念
程序在内存中运行,文件在磁盘上,把文件从磁盘上读入内存中来,当然是输入流了, 反之,把内存中的数据写到磁盘上的文件里面去就是输出.通常都是这样的,用了过滤流的情况复杂一些,则另当别论.
- Ubuntu下编译C++ OpenCV程序并运行
因为想试跑yolov3的缘故,所以装了ubuntu系统,直接通过U盘装的,并不像他们说的“折腾”,反而一切非常顺利,比装软件还简单.然后就是要用C++跑opencv的程序用于比赛,出于 ...
- Raid 6与raid 5的区别
RAID5和RAID6有下面几个区别: 1.冗余和数据恢复能力 RAID组级别 冗余及数据恢复能力 数据恢复策略 RAID 5 存在分散在不同条带上的奇偶校验数据 允许一块数据盘故障,并可通过奇偶校验 ...
- 一、git提交代码步骤
git add . //添加到本地缓存区 git commit -m '备注 ' //提交代码到本地仓库 git push //将本地代码推送到develop分支上 git checkout m ...
- (原)IPhone开发时把ToolBar中的元素居中的技巧
在IPhone应用开发时,经常用到ToolBar,其中的控件通常都是居左,想让它居中就有点困难. 这里介绍一种方法: 将Flexible Space Bar Button Item从库中拖到位于控件左 ...
- CORS的原理及应用
CORS的原理及应用 CORS是跨站资源共享,同样是解决浏览器的同源策略 其本质是设置响应头,使得浏览器允许跨域请求. 第三方网站返回数据的时候在浏览器的响应头中添加允许的域名,允许所有的用* 1 简 ...
- 标签input的value属性和placeholderde 区别
placeholder 顾名思义是一个占位符 在你的value为空的时候他才会显示出来,但是他本身并不是value,也不会被表单提交.
- iOS 解决ipv6问题
解决ipv6的方法有很多种,由于现在国内的网络运营商还在使用ipv4的网络环境,所以appstore应用不可能大范围去修改自己的服务器, 而且国内的云服务器几乎没有ipv6地址. 这里附上苹果开发平台 ...
- 关于UINavigationController的一些技巧
未自定义任何东西的导航条效果如下: 1.自定义了 leftBarButtonItem 之后,左滑返回手势失效了,解决办法: self.navigationController.interactiveP ...
- thinkcmf常用标签
1.图片地址:{:cmf_get_image_url($vo.icon)} 2.模板控件 模板变量调用:$theme_vars.title <widget name="aboutUs& ...