【树链剖分】【线段树】bzoj3626 [LNOI2014]LCA
引用题解:
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;