标签:
引用题解:
http://blog.csdn.net/popoqqq/article/details/38823457
题目大意:
给出一个n个节点的有根树(编号为0到n-1,根节点为0)。一个点的深度定义为这个节点到根的距离+1。
设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先。
有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[LCA(i,z)]。
(即,求在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和)这题看见了直接卡壳。。。然后看了题解才搞懂要怎么写。。。
直接复制gconeice的题解吧
显然,暴力求解的复杂度是无法承受的。
考虑这样的一种暴力,我们把 z 到根上的点全部打标记,对于 l 到 r 之间的点,向上搜索到第一个有标记的点求出它的深度统计答案。观察到,深度其实就是上面有几个已标记了的点(包括自身)。所以,我们不妨把 z 到根的路径上的点全部 +1,对于 l 到 r 之间的点询问他们到根路径上的点权和。仔细观察上面的暴力不难发现,实际上这个操作具有叠加性,且可逆。也就是说我们可以对于 l 到 r 之间的点 i,将 i 到根的路径上的点全部 +1, 转而询问 z 到根的路径上的点(包括自身)的权值和就是这个询问的答案。把询问差分下,也就是用 [1, r] − [1, l − 1] 来计算答案,那么现在我们就有一个明显的解法。从 0 到 n − 1 依次插入点 i,即将 i 到根的路径上的点全部+1。离线询问答案即可。我们现在需要一个数据结构来维护路径加和路径求和,显然树链剖分或LCT 均可以完成这个任务。树链剖分的复杂度为 O((n + q)· log n · log n),LCT的复杂度为 O((n + q)· log n),均可以完成任务。至此,题目已经被我们完美解决。写的很详细,我就不累述了
同样是14年的省选,同样是树链剖分,为啥辽宁省的就如此牛B,吉林省的就是水题。。。差距好大0.0
取模的话数字不是很大 可以开long long 最后取一下模就好了 中途不要取模 弄成负的WA了我一下午0.0 又一下午0.0 又一下午0.0 啊啊啊啊啊啊啊啊啊
——PoPoQQQ
#include<cstdio>
#include<algorithm>
using namespace std;
#define N 50001
#define lson rt<<1,l,m
#define rson rt<<1|1,m+1,r
struct ASK{int p,z,id;}Q[N<<1];
int n,m;
bool operator < (const ASK &a,const ASK &b){return a.p<b.p;}
int sumv[N<<2],delta[N<<2];
void pushdown(int rt,int sz)
{
if(delta[rt])
{
sumv[rt<<1]+=(sz-(sz>>1))*delta[rt];
sumv[rt<<1|1]+=(sz>>1)*delta[rt];
delta[rt<<1]+=delta[rt];
delta[rt<<1|1]+=delta[rt];
delta[rt]=0;
}
}
void update(int ql,int qr,int rt,int l,int r)
{
if(ql<=l&&r<=qr)
{
sumv[rt]+=(r-l+1);
++delta[rt];
return;
}
pushdown(rt,r-l+1);
int m=(l+r>>1);
if(ql<=m) update(ql,qr,lson);
if(m<qr) update(ql,qr,rson);
sumv[rt]=sumv[rt<<1]+sumv[rt<<1|1];
}
int query(int ql,int qr,int rt,int l,int r)
{
if(ql<=l&&r<=qr) return sumv[rt];
pushdown(rt,r-l+1);
int m=(l+r>>1),res=0;
if(ql<=m) res+=query(ql,qr,lson);
if(m<qr) res+=query(ql,qr,rson);
return res;
}
int v[N],en,first[N],next[N];
void AddEdge(int U,int V)
{
v[++en]=V;
next[en]=first[U];
first[U]=en;
}
int top[N],tot,Num[N],fa[N],siz[N],son[N];
void dfs(int U)
{
siz[U]=1;
for(int i=first[U];i;i=next[i])
{
dfs(v[i]);
siz[U]+=siz[v[i]];
if(siz[v[i]]>siz[son[U]])
son[U]=v[i];
}
}
void df2(int U)
{
Num[U]=++tot;
if(son[U])
{
top[son[U]]=top[U];
df2(son[U]);
}
for(int i=first[U];i;i=next[i])
if(v[i]!=son[U])
{
top[v[i]]=v[i];
df2(v[i]);
}
}
void Update(int U)
{
while(top[U]!=1)
{
update(Num[top[U]],Num[U],1,1,n);
U=fa[top[U]];
}
update(1,Num[U],1,1,n);
}
int Query(int U)
{
int res=0;
while(top[U]!=1)
{
res+=query(Num[top[U]],Num[U],1,1,n);
U=fa[top[U]];
}
return res+query(1,Num[U],1,1,n);
}
int pre[N],ans[N];
int main()
{
int x;
scanf("%d%d",&n,&m);
for(int i=2;i<=n;++i)
{
scanf("%d",&x);
AddEdge(x+1,i);
fa[i]=x+1;
}
top[1]=1;
dfs(1);
df2(1);
for(int i=1;i<=m;++i)
{
scanf("%d%d%d",&Q[(i<<1)-1].p,&Q[i<<1].p,&Q[(i<<1)-1].z);
++Q[(i<<1)-1].z;
++Q[i<<1].p;
Q[(i<<1)-1].id=(i<<1)-1;
Q[i<<1].z=Q[(i<<1)-1].z;
Q[i<<1].id=(i<<1);
}
sort(Q+1,Q+(m<<1|1));
for(int i=1;i<=(m<<1);++i)
{
for(int j=Q[i-1].p+1;j<=Q[i].p;++j)
Update(j);
if(!(Q[i].id&1)) ans[Q[i].id>>1]=Query(Q[i].z)-pre[Q[i].id>>1];
else pre[(Q[i].id+1)>>1]=Query(Q[i].z);
}
for(int i=1;i<=m;++i)
printf("%d\n",ans[i]%201314);
return 0;
【树链剖分】【线段树】bzoj3626 [LNOI2014]LCA
标签:
原文地址:http://www.cnblogs.com/autsky-jadek/p/4346165.html