树链剖分bzoj3626

渐渐的我也能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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值