[USACO11DEC]牧草种植Grass Planting——[树链剖分]

【原题】
Farmer John has N barren pastures (2 <= N <= 100,000) connected by N-1 bidirectional roads, such that there is exactly one path between any two pastures. Bessie, a cow who loves her grazing time, often complains about how there is no grass on the roads between pastures. Farmer John loves Bessie very much, and today he is finally going to plant grass on the roads. He will do so using a procedure consisting of M steps (1 <= M <= 100,000).

At each step one of two things will happen:

FJ will choose two pastures, and plant a patch of grass along each road in between the two pastures, or,

Bessie will ask about how many patches of grass on a particular road, and Farmer John must answer her question.

Farmer John is a very poor counter – help him answer Bessie’s questions!

【题目翻译】

农夫约翰有N个贫瘠的牧场 (2 <= N <= 100,000),通过N-1条道路连接,确认每两个牧场之间只有一条路。贝茜是一只喜欢在牧场上享受牧草的奶牛,她经常抱怨牧场之间的路上没有牧草。农夫约翰很喜欢贝茜,今天他终于打算在道路上种草了!!!!他和贝茜要进行M次操作(1 <= M <= 100,000).

每步操作中,FJ会在x,y(1<=x,y<=n)之间的路径上种一单位的草,或者贝茜询问x,y(1<=x,y<=n)之间的牧草有几个单位。

农夫约翰不会数数,所以他找到你来回答贝茜的问题!

【输入格式】
第一行:N和M
第2-N行:两个数,表示这两个牧场之间有路连接。
第N+1-N+M行:先是一个字符,如果是P,则表示种植,如果是Q,则表示询问,然后是两个数,表示种植(询问)的起点与终点。

【输出格式】
M行,表示每个询问的答案。

【样例输入】
4 6
1 4
2 4
3 4
P 2 3
P 1 3
Q 3 4
P 1 4
Q 2 4
Q 1 4

【样例输出】
2
1
2

【题意分析】
上面是蒟蒻翻译的qwq
简化题意:给你一棵树,需要支持路径修改与路径查询(用的是边权)

平常我们的树剖用的都是点权,但是这道题要用的是边权,其实处理也很简单:因为一条边连的是两条边,我们就可以把边权给那个深度更加深的节点,上面的节点就不给他了。

这样修改之后,我们线段树update和query的时候,根节点左儿子是2,右儿子是n(因为节点1什么都没给他),树剖的时候,如果两个点一模一样了(是一条轻边),那就没必要query或者update,因为轻链退化成了最近公共祖先,而上面什么都没有,因此不能要。具体见代码。

蒟蒻吐槽:查了半天的错误,一开始数组开到100020就死活过不去,一气之下开了200000,,,就过了???因此空间优裕的情况下,还是不要吝惜内存。

Code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAX 200000
using namespace std;

struct Front_Link_Star{
	int next,to;
}edge[MAX];

int tree[MAX << 2],lazy[MAX << 2],head[MAX],top[MAX],son[MAX],depth[MAX];
int id[MAX],father[MAX],size[MAX],n,m,res,cnt,dfn;

inline void Add_Edge(int u,int v){
	edge[++cnt].to=v;
	edge[cnt].next=head[u];
	head[u]=cnt;
}

inline void push_down(int now,int tl,int tr){
	int mid=(tl+tr) >> 1;
	lazy[now << 1]+=lazy[now];
	lazy[now << 1|1]+=lazy[now];
	tree[now << 1]+=lazy[now]*(mid-tl+1);
	tree[now << 1|1]+=lazy[now]*(tr-mid);
	lazy[now]=0;
}    //线段树标记下传

inline void update(int now,int tl,int tr,int left,int right,int change){
	if (right<tl||tr<left)return;
	if (left<=tl&&tr<=right){
		tree[now]+=change*(tr-tl+1);
		lazy[now]+=change;
		return;
	}
	if (lazy[now])push_down(now,tl,tr);
	int mid=(tl+tr) >> 1;
	update(now << 1,tl,mid,left,right,change);
	update(now << 1|1,mid+1,tr,left,right,change);
	tree[now]=tree[now << 1]+tree[now << 1|1];
}     //修改

inline void query(int now,int tl,int tr,int left,int right){
	if (right<tl||tr<left)return;
	if (left<=tl&&tr<=right){
		res+=tree[now];
		return;
	}
	if (lazy[now])push_down(now,tl,tr);
	int mid=(tl+tr) >> 1;
	query(now << 1,tl,mid,left,right);
	query(now << 1|1,mid+1,tr,left,right);
}      //查询

inline void Modify_Range(int x,int y,int change){
	while (top[x]!=top[y]){
		if (depth[top[x]]<depth[top[y]])swap(x,y);
		update(1,2,n,id[top[x]],id[x],change);
		x=father[top[x]];
	}
	if (x==y)return;     //两点重合了,就是轻边
	if (depth[x]>depth[y])swap(x,y);
	update(1,2,n,id[x]+1,id[y],change);
	//update要注意,因为我们换成了点权,所以要从id[x]+1
	开始
}     

inline int Query_Range(int x,int y){
	res=0;
	while (top[x]!=top[y]){
		if (depth[top[x]]<depth[top[y]])swap(x,y);
		query(1,2,n,id[top[x]],id[x]);
		x=father[top[x]];
	}
	if (x==y)return res;
	if (depth[x]>depth[y])swap(x,y);
	query(1,2,n,id[x]+1,id[y]);
	return res;
}    //同上

inline void DFS1(int now,int fa,int d){
	father[now]=fa;
	depth[now]=d;
	size[now]=1;
	int maxson=-1;
	for (register int i=head[now];i;i=edge[i].next){
		int v=edge[i].to;
		if (v==fa)continue;
		DFS1(v,now,d+1);
		size[now]+=size[v];
		if (size[v]>maxson){
			maxson=size[v];
			son[now]=v;
		}
	}
}

inline void DFS2(int now,int top_heavy){
	top[now]=top_heavy;
	id[now]=++dfn;
	if (!son[now])return;
	DFS2(son[now],top_heavy);
	for (register int i=head[now];i;i=edge[i].next){
		int v=edge[i].to;
		if (v!=father[now]&&v!=son[now])DFS2(v,v);
	}
}    //树剖

int main(){
	scanf("%d%d",&n,&m);
	for (register int i=1;i<n;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		Add_Edge(x,y);
		Add_Edge(y,x);
	}
	DFS1(1,1,1);
	//其实这里DFS1(1,0,1)也是等价的,但从道理来说应该是DFS1(1,1,1)
	DFS2(1,1);
	while (m--){
		char ch[4];
		int x,y;
		scanf("%s%d%d",&ch,&x,&y);
		if (ch[0]=='P')Modify_Range(x,y,1);
		if (ch[0]=='Q')printf("%d\n",Query_Range(x,y));
	}
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值