您的位置:首页 > 其它

对于树链剖分一个细节的解释 BSOJ3690:Grass Planting种草

2016-02-16 00:01 330 查看
Description

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条双向道路将这N个牧场连接了起来,每两个牧场间都有且仅有一条路径可相互到达。著名奶牛贝西经常抱怨:为什么连接牧场的道路上没有草可吃呢?

约翰非常喜欢贝西,今天约翰终于决定要在道路上种草了。约翰的种草工作被分成了M(1 <= M <=100,000)步操作。

在每一步中,下列两个事件中的一个会发生:

1.约翰会选择两个牧场,沿着两个牧场间的路径,在路径上的每一条道路上都种植1株牧草;

2.贝西会向约翰提问:在一条指定的道路上,种植了多少株牧草;

请帮助约翰回答贝西的问题。

Input

* Line 1: Two space-separated integers N and M.

* Lines 2..N: Two space-separated integers describing the endpoints of a road.

* Lines N+1..N+M: Line i+1 describes step i. The first character of the line is either P or Q, which describes whether or not FJ is planting grass or simply querying. This is followed by two space-separated integers A_i and B_i (1 <= A_i, B_i <= N) which describe
FJ's action or query.

第一行,两个空格间隔的整数N和M 接下来N-1行,每行两个整数x和y,表示牧场x和y之间有道路直接相连

接下来M行,每行描述一步操作:

每行以字母P或Q作为开头,P代表种草操作,Q代表询问操作,接下来两个整数,A_i 和 B_i用于描述该步的操作(1 <= A_i, B_i <= N)。

Output

* Lines 1..???: Each line has the answer to a query, appearing in the same order as the queries appear in the input.

对于每一次询问,输出一行,一个整数,表示询问的答案

Sample Input

4 61 42 43 4P 2 3P 1 3Q 3 4P 1 4Q 2 4Q 1 4

Sample Output

212

这是一道树链剖分可以解决的题,显而易见,区间修改,单点查询。

我们把边权存在两点中深度较大的点上,就可以把询问边权改为询问点权。

我想解释的细节是:

1.两重链靠拢操作后,为什么有时候要if (x==y) return; ?

2.两重链靠拢操作后还要进行次递归,为什么有时传的是tid[son[x]],有时是tid[x]?

以下是我的想法:


1.靠拢后,x与y在同一条重链上,对于一些修改操作,如加减要改变权值的,若xy已经是同一个点,则不能再次修改,否则就会起到相反的效果或者多做了一次。

2.边权存在深度大点上,即son[x],画一个简单的图如下。

Edge 1存在2上;

Edge 2存在3上;

Edge 3存在4上;

现在,修改Edge 1-->3?

显然,修改的是2,3,4节点。

x y靠到同一条重链上后,中间还隔了一段,要操作这一段,实际操作的是tid[son[x]]-->tid[y]节点。

下面附上代码和注释易错

#include<iostream>
#include<cstdio>
#include<cstring>
#define L(x) (x<<1)//
#define R(x) (x<<1|1)//
#define G 400005
using namespace std;
struct Edge
{
int to,next;
}w[G*2];
struct SegmentTree
{
int l,r,sum,lazy;
}tree[G];
int fa[G]={0},tid[G]={0},rank[G]={0},dep[G]={0},son[G]={0},size[G]={0},top[G]={0};
int tim=0,cnt=0,n,m;
int h[G]={0};
void add(int x,int y)
{
cnt++;w[cnt].to=y;w[cnt].next=h[x];h[x]=cnt;
}
void DFS1(int x,int f,int d)
{
dep[x]=d;
fa[x]=f;
size[x]=1;
for(int i=h[x];i;i=w[i].next)
{
int to=w[i].to;
if(to!=f)
{
DFS1(to,x,d+1);
size[x]+=size[to];
if(son[x]==0||size[son[x]]<size[to])son[x]=to;
}
}
}
void DFS2(int x,int tp)
{
top[x]=tp;
tid[x]=++tim;
if(son[x]==0)return;
DFS2(son[x],tp);
for(int i=h[x];i;i=w[i].next)
{
int to=w[i].to;
if(to!=son[x]&&to!=fa[x])DFS2(to,to);
}
}
void Pushup(int x)
{
tree[x].sum=tree[L(x)].sum+tree[R(x)].sum;
}
void Pushdown(int x)
{
if(tree[x].lazy!=0)
{
int d=tree[x].lazy;
tree[L(x)].sum+=d;
tree[R(x)].sum+=d;
tree[L(x)].lazy+=d;
tree[R(x)].lazy+=d;
tree[x].lazy=0;
}
}
void Build(int l,int r,int root)
{
tree[root].l=l;tree[root].r=r;
tree[root].sum=tree[root].lazy=0;
if(l==r)return;
int mid=(l+r)>>1;
Build(l,mid,L(root));
Build(mid+1,r,R(root));
Pushup(root);
}
void update(int l,int r,int root,int data)
{
Pushdown(root);
if(l<=tree[root].l&&tree[root].r<=r)
{
tree[root].sum+=data;
tree[root].lazy+=data;
return;
}

int mid=(tree[root].l+tree[root].r)>>1;
if(l<=mid)update(l,r,L(root),data);
if(r>mid)update(l,r,R(root),data);
Pushup(root);
}
void Change(int x,int y,int v)
{
int t1=top[x],t2=top[y];
while(t1!=t2)
{
if(dep[t1]<dep[t2]){swap(t1,t2);swap(x,y);}
update(tid[t1],tid[x],1,v);
x=fa[t1];t1=top[x];
}
if(x==y)return;
if(dep[x]>dep[y])swap(x,y);
update(tid[son[x]],tid[y],1,v);
}
int ans=0;
int Getsum(int x,int root)
{Pushdown(root);
if(tree[root].l==tree[root].r){return tree[root].sum;}

int mid=(tree[root].l+tree[root].r)>>1;
if(x<=mid)return Getsum(x,L(root));
else return Getsum(x,R(root));
Pushup(root);

}
int main(){
scanf("%d%d",&n,&m);
int a,b;
for(int i=1;i<n;i++)
{
scanf("%d%d",&a,&b);
add(a,b);add(b,a);
}
DFS1(1,0,1);
DFS2(1,1);
Build(1,n,1);
char cmd;
while(m--)
{
scanf("%s%d%d",&cmd,&a,&b);
if(cmd=='P')Change(a,b,1);
if(cmd=='Q')
{
if(dep[a]<dep[b])swap(a,b);
printf("%d\n",Getsum(tid[a],1));
}
}
return 0;
}


哈 其实没什么好注释的
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: