BZOJ 3626 LNOI 2014 LCA 树链剖分

来源:互联网 发布:手机视频制作软件 编辑:程序博客网 时间:2024/05/18 14:44

题目大意:给出一棵树,有n个问题,询问在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和。


思路:不会,然后看了题解,之后发现自己智商严重不足。

看到数据范围就知道一定要离线处理,就这个离线处理我估计以我的智商不看题解是肯定想不出来的。。

考虑这样的一种暴力,我们把 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),均可以完成任务。至此,题目已经被我们完美解决。

神题啊。。。


CODE:

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#define MAX 100010#define INF 0x3f3f3f3f#define LEFT (pos << 1)#define RIGHT (pos << 1|1)#define CNT (r - l + 1)#define MO 201314using namespace std;struct _Ask{bool is_l;int x,z;int id;_Ask(bool _,int __,int ___,int ____):is_l(_),x(__),z(___),id(____) {}_Ask() {}bool operator <(const _Ask &a)const {if(x == a.x)return is_l;return x < a.x;}}ask[MAX];struct SegTree{long long sum;int c;}tree[MAX << 2];int points,asks;int head[MAX],total;int next[MAX],aim[MAX];int deep[MAX],father[MAX],son[MAX],size[MAX];int pos[MAX],top[MAX],cnt;long long ans[MAX];inline void Add(int x,int y){next[++total] = head[x];aim[total] = y;head[x] = total;}void PreDFS(int x){deep[x] = deep[father[x]] + 1;size[x] = 1;int max_size = 0,p = 0;for(int i = head[x]; i; i = next[i]) {PreDFS(aim[i]);size[x] += size[aim[i]];if(max_size < size[aim[i]])max_size = size[aim[i]],p = aim[i];}son[x] = p;}void DFS(int x,int _top){pos[x] = ++cnt;top[x] = _top;if(son[x])DFS(son[x],_top);for(int i = head[x]; i; i = next[i]) {if(aim[i] == son[x])continue;DFS(aim[i],aim[i]);}}inline void PushDown(int pos,int cnt){if(tree[pos].c) {tree[LEFT].sum += tree[pos].c * (cnt - (cnt >> 1));tree[RIGHT].sum += tree[pos].c * (cnt >> 1);tree[LEFT].c += tree[pos].c;tree[RIGHT].c += tree[pos].c;tree[pos].c = 0;}}void Modify(int l,int r,int x,int y,int pos){if(l == x && r == y) {tree[pos].sum += CNT;++tree[pos].c;return ;}PushDown(pos,CNT);int mid = (l + r) >> 1;if(y <= mid)Modify(l,mid,x,y,LEFT);else if(x > mid)Modify(mid + 1,r,x,y,RIGHT);else {Modify(l,mid,x,mid,LEFT);Modify(mid + 1,r,mid + 1,y,RIGHT);}tree[pos].sum = tree[LEFT].sum + tree[RIGHT].sum;}inline void Modify(int x){while(x) {Modify(1,cnt,pos[top[x]],pos[x],1);x = father[top[x]];}}long long Ask(int l,int r,int x,int y,int pos){if(l == x && y == r)return tree[pos].sum;PushDown(pos,CNT);int mid = (l + r) >> 1;if(y <= mid)return Ask(l,mid,x,y,LEFT);if(x > mid)return Ask(mid + 1,r,x,y,RIGHT);long long left = Ask(l,mid,x,mid,LEFT);long long right = Ask(mid + 1,r,mid + 1,y,RIGHT);return left + right;}inline long long Ask(int x){long long re = 0;while(x) {re += Ask(1,cnt,pos[top[x]],pos[x],1);x = father[top[x]];}return re;}int main(){cin >> points >> asks;for(int x,i = 2; i <= points; ++i) {scanf("%d",&x),++x;Add(x,i);father[i] = x;}PreDFS(1);DFS(1,1);for(int num = 0,x,y,z,i = 1; i <= asks; ++i) {scanf("%d%d%d",&x,&y,&z);++x,++y,++z;ask[++num] = _Ask(true,x - 1,z,i);ask[++num] = _Ask(false,y,z,i);}sort(ask + 1,ask + (asks << 1) + 1);int j = 1;for(int i = 0; i <= points; ++i) {Modify(i);for(; ask[j].x == i; ++j)if(ask[j].is_l)ans[ask[j].id] = Ask(ask[j].z);elseans[ask[j].id] = (Ask(ask[j].z) - ans[ask[j].id]) % MO;}for(int i = 1; i <= asks; ++i)printf("%d\n",(int)ans[i]);return 0;}


0 0
原创粉丝点击