树链剖分bzoj3626

来源:互联网 发布:java class method 编辑:程序博客网 时间:2024/05/16 19:24

渐渐的我也能1a了哈哈哈!!!!!(立个flag只要我1a的题,题解就写详细点)

比如说一颗树 1-2 2-3 2-4 3-5现在叫你求lca(5,4)的deep 你阔以这样求 先树链剖分,然后从1到5的点上的value都加1(当然初始化的时候每个点上的value=0),这样做的目的在于将深度进行转化,那么现在就相当于让你求1-4这条路径上的value和,嗯,现在问题得到了转化,就阔以加线段树了2333.

然后此题就是一个离线做法,(不懂离线是什么?)具体看代码。

#include<iostream>#include<cstdio>#include<algorithm>using namespace std;struct edgee{int from, to;edgee(int f, int t) :from(f), to(t){}edgee(){}};edgee edge[120000];int edgetot,first[60000], nextt[120000],size[60000];int enumm[60000];//enum[i]表示从i的父亲到i的边在线段树上的位置int add[180000];//管理线段树用的long long sum[180000];//管理线段树用的int n,q;void addedge(int from, int to){edge[edgetot] = edgee(from, to);nextt[edgetot] = first[from];first[from] = edgetot++;edge[edgetot] = edgee(to, from);nextt[edgetot] = first[to];first[to] = edgetot++;    }int son[60000],father[60000],top[60000],cuttot=0;void getson(int num, int fa)//树链剖分的求重儿子的代码{son[num] = -1, father[num] = fa; int temp = -1;for (int i = first[num]; i != -1; i = nextt[i]){int to = edge[i].to;if (to == fa)continue;getson(to, num);size[num] += size[to];if (temp < size[to])temp = size[to], son[num] = i;}}void getcut(int num, int fa,int &tot,int to)//开始进行正式的剖分{enumm[num] = tot; tot++; top[num] = to;if (son[num] == -1)return;getcut(edge[son[num]].to, num, tot,to);for (int i = first[num]; i!= -1; i = nextt[i]){int to = edge[i].to;if (to == fa || to == edge[son[num]].to)continue;getcut(to, num, tot,to);}}void radd(int l, int r, int temp, int ll, int rr, int value)//线段树模板{if (ll >= l&&rr <= r)add[temp] += value;else{int mid = (ll + rr) >> 1;if (mid >= l)radd(l, r, (temp << 1), ll, mid, value);if (mid + 1 <= r)radd(l, r, (temp << 1) | 1, mid + 1, rr, value);sum[temp] += (long long )((min(rr, r) - max(ll, l) + 1)*value);}}long long summ(int l, int r, int temp, int ll, int rr,int addtot)//线段树模板{if (ll >= l&&rr <= r)return sum[temp] + (add[temp]+addtot)* (rr - ll + 1);else{int mid = (ll + rr) >> 1;long long lsum = 0, rsum = 0;if (mid >= l)lsum = summ(l, r, (temp << 1), ll, mid,addtot+add[temp]);if (mid + 1 <= r)rsum = summ(l, r, (temp << 1) | 1, mid + 1, rr,addtot+add[temp]);return lsum + rsum;}}void cadd(int num)//从此点一直加到根,让路径上的所有点的value都加1{while (num != -1){radd(enumm[top[num]], enumm[num], 1, 0, cuttot - 1,1);num = father[top[num]];}return;}long long csum(int num)//从此点一直到根,求路径上的所有点的value和{long long ans = 0;while (num != -1){ans += summ(enumm[top[num]], enumm[num], 1, 0, cuttot - 1,0);num = father[top[num]];}return ans;}struct qq{int l,z,kind;int belong;};qq query[120000];long long ans[120000];bool com(qq a, qq b)//这为离线做准备{return a.l < b.l;}int main(){scanf("%d%d", &n, &q);for (int i = 0; i < n; i++)first[i] = -1;for (int i = 1; i < n; i++){int b;scanf("%d", &b);addedge(b, i);}cuttot = 0;getson(0, -1);getcut(0, -1, cuttot, 0);int tot = 0;for (int i = 0; i < q; i++){int a, b, c;scanf("%d%d%d", &a, &b, &c);//我们在这里将询问分成两个query[tot].l = a - 1; query[tot].z = c; query[tot].belong = i; query[tot++].kind = -1;//kind=1那么就是加上这个答案,如果是-1就减去query[tot].l = b; query[tot].z = c; query[tot].belong = i; query[tot++].kind = 1;//注意我的belong }sort(query, query + tot, com);//这个是离线的关键。int all = 0; int temp = 0;while (all < tot){int l = all, r = all;while (query[r].l == query[r + 1].l&&r<tot-1)r++;for (; temp <= query[r].l; temp++)//依次更新从0-n-1到根路径上的哪些点的valuecadd(temp);while (l <= r){if (query[l].l == -1){l++;continue;}ans[query[l].belong] += csum(query[l].z)*query[l].kind, l++;}all = l;}for (int i = 0; i < q; i++)printf("%lld\n", ans[i] % 201314);return 0;}