您的位置:首页 > 其它

[BZOJ3626][LNOI2014]LCA(离线+差分+树链剖分)

2016-05-12 17:13 477 查看

题目描述

传送门

题解

首先把询问lr拆成两个询问。

利用差分的思想,每次查询区间[1,i]与zlca的和,然后用r的值减去l-1的值就是答案。

查询区间[1,i]与zlca的和,可以将1-i所有的点到根的路径上的点都+1,然后查询z到根的路径上所有点的权值和即为答案。其实这就是深度的表示。

那么离线询问之后排序,时间复杂度为O(nlogn)

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;

const int max_n=5e4+5;
const int max_e=max_n*2;
const int max_tree=max_n*4;
const int Mod=201314;

int n,q,fa,l,r,z,N,u,t,cnt;
int tot,point[max_n],nxt[max_e],v[max_e];
int size[max_n],top[max_n],h[max_n],father[max_n],son[max_n],num[max_n];
int sum[max_tree],delta[max_tree];
struct hp{int pt,z,id,f;}ask[max_e];
int ans[max_n][3];

inline void addedge(int x,int y)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
}
inline int cmp(hp a,hp b)
{
return a.pt<b.pt;
}
inline void dfs_1(int x,int fa,int dep)
{
size[x]=1; h[x]=dep; father[x]=fa;
int maxson=0;
for (int i=point[x];i;i=nxt[i])
if (v[i]!=fa)
{
dfs_1(v[i],x,dep+1);
size[x]+=size[v[i]];
if (size[v[i]]>maxson)
{
maxson=size[v[i]];
son[x]=v[i];
}
}
}
inline void dfs_2(int x,int fa)
{
if (son[fa]==x) top[x]=top[fa];
else top[x]=x;
num[x]=++N;
if (son[x]) dfs_2(son[x],x);
for (int i=point[x];i;i=nxt[i])
if (v[i]!=fa&&v[i]!=son[x])
dfs_2(v[i],x);
}
inline void update(int now)
{
sum[now]=sum[now<<1]+sum[now<<1|1];
}
inline void pushdown(int now,int l,int r,int mid)
{
if (delta[now])
{
sum[now<<1]+=delta[now]*(mid-l+1);
delta[now<<1]+=delta[now];
sum[now<<1|1]+=delta[now]*(r-mid);
delta[now<<1|1]+=delta[now];
delta[now]=0;
}
}
inline void interval_change(int now,int l,int r,int lrange,int rrange,int v)
{
int mid=(l+r)>>1;
if (lrange<=l&&r<=rrange)
{
sum[now]+=v*(r-l+1);
delta[now]+=v;
return;
}
pushdown(now,l,r,mid);
if (lrange<=mid)
interval_change(now<<1,l,mid,lrange,rrange,v);
if (mid+1<=rrange)
interval_change(now<<1|1,mid+1,r,lrange,rrange,v);
update(now);
}
inline int query(int now,int l,int r,int lrange,int rrange)
{
int mid=(l+r)>>1,ans=0;
if (lrange<=l&&r<=rrange) return sum[now];
pushdown(now,l,r,mid);
if (lrange<=mid)
ans+=query(now<<1,l,mid,lrange,rrange);
if (mid+1<=rrange)
ans+=query(now<<1|1,mid+1,r,lrange,rrange);
return ans;
}
int main()
{
scanf("%d%d",&n,&q);
for (int i=2;i<=n;++i)
{
scanf("%d",&fa);
fa++;
addedge(fa,i);
}
dfs_1(1,0,1);
dfs_2(1,0);
for (int i=1;i<=q;++i)
{
scanf("%d%d%d",&l,&r,&z);
++l; ++r; ++z;
ask[++cnt].pt=l-1; ask[cnt].z=z; ask[cnt].id=i; ask[cnt].f=1;
ask[++cnt].pt=r; ask[cnt].z=z; ask[cnt].id=i; ask[cnt].f=2;
}
sort(ask+1,ask+cnt+1,cmp);
for (int i=1;i<=cnt;++i)
{
for (int j=ask[i-1].pt+1;j<=ask[i].pt;++j)
{
u=1; t=j;
int f1=top[u],f2=top[t];
while (f1!=f2)
{
if (h[f1]<h[f2])
{
swap(f1,f2);
swap(u,t);
}
interval_change(1,1,N,num[f1],num[u],1);
u=father[f1];
f1=top[u];
}
if (num[u]>num[t]) swap(u,t);
interval_change(1,1,N,num[u],num[t],1);

}
u=1; t=ask[i].z;
int f1=top[u],f2=top[t];
while (f1!=f2)
{
if (h[f1]<h[f2])
{
swap(f1,f2);
swap(u,t);
}
ans[ask[i].id][ask[i].f]+=query(1,1,N,num[f1],num[u]);
u=father[f1];
f1=top[u];
}
if (num[u]>num[t]) swap(u,t);
ans[ask[i].id][ask[i].f]+=query(1,1,N,num[u],num[t]);
}
for (int i=1;i<=q;++i)
printf("%d\n",(ans[i][2]-ans[i][1]+Mod)%Mod);
}


总结

想到了离线,想到了差分,想到了标记一个查询一个,但是没有想到深度标记的表示。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: