渐渐的我也能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到根路径上的哪些点的value
cadd(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;
}