洛谷 P3038 [USACO11DEC]牧草种植Grass Planting

洛谷传送门

JDOJ 2282: USACO 2011 Dec Gold 3.Grass Planting

JDOJ传送门

Description

Problem 3: Grass Planting [Travis Hance, 2011]

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!

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.

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 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

Sample Output

2 1 2

Source

2011~2012

题目大意:

给定一个有N个节点的树及M个操作:P操作把一条路径上的边权加一,Q操作询问一条路径上的权值和。

题解:

一道树链剖分的题。(板子题都是紫题了为啥这题还是蓝?)

难点有二:

一:边权转点权。

二:路径和的查询。

对于对树链剖分不是和熟悉的小伙伴,推荐下面的这篇博客,里面有对树链剖分的详细讲解。

浅谈树链剖分

我们会发现,树链剖分是一个对点权进行操作的东西。

而题目要求是边权。

所以我们要把边权转点权(废话)。

怎么转呢?

我们会发现一条边连着两个点,因为题目保证给出的是一棵树。我们会发现:一个点可能会有很多个儿子(也就是说有很多连着儿子的边),但是每个儿子只有一个父亲。对应地一层一层类比,我们会发现到叶子节点的时候,它连儿子也没有。

那么我们就可以考虑把边权变成其儿子节点的点权,那么根节点的点权为0,而其他所有边的边权都被映射到了一个点上。

截至这里,我们解决了第一个问题。

如果我们到这里就完事的话,我们最终会全WA。

为什么呢?

还是因为题目中询问的是边权。如果是一个路径的话,比如题目中想求得两条边的权值,但是我们映射完了之后会求得3个点的点权,也就意味着,我们多搞了一个答案。这显然是不符合正确性的。

因为我们是向子节点映射,所以我们多加的其实是目标点的LCA那个点的点权。

所以我们考虑把LCA那个点的点权减掉。

那么我们回顾一下树链剖分求LCA的过程(如有不懂还是翻上面的那篇博客)

我们跳来跳去,最后跳的那一次就能求到LCA,也就是说,我们为了维护查询的正确性,需要把最后一次跳跃的更浅的那个节点变成它的子节点。这样就能保证,LCA不会被加进来。

这个操作的正确性是显然的。因为LCA跳到最后,两个节点必然在一条重链上。而根据树链剖分的预处理操作,一条重链上的节点编号是连续的,那么我们寻找LCA进行跳跃的时候,id[x]就是我们的LCA,那么我们把它+1,就会得到它的儿子节点,这样,LCA就不会被加进来。

代码加深理解:

#include<cstdio>
#include<algorithm>
#include<iostream>
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#define lson pos<<1
#define rson pos<<1|1
using namespace std;
const int maxn=1e5+1;
int n,m,tot,cnt;
int a[maxn];
int head[maxn],nxt[maxn<<1],to[maxn<<1];
int deep[maxn],size[maxn],son[maxn],fa[maxn];
int top[maxn],id[maxn],w[maxn];
int tree[maxn<<2],lazy[maxn<<2];
void add(int x,int y)
{
to[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void dfs1(int x,int f)
{
deep[x]=deep[f]+1;
size[x]=1;
fa[x]=f;
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(y==f)
continue;
dfs1(y,x);
size[x]+=size[y];
if(!son[x]||size[y]>size[son[x]])
son[x]=y;
}
}
void dfs2(int x,int t)
{
id[x]=++cnt;
w[cnt]=a[x];
top[x]=t;
if(!son[x])
return;
dfs2(son[x],t);
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(y==fa[x]||y==son[x])
continue;
dfs2(y,y);
}
}
void build(int pos,int l,int r)
{
int mid=(l+r)>>1;
if(l==r)
{
tree[pos]=w[l];
return;
}
build(lson,l,mid);
build(rson,mid+1,r);
tree[pos]=tree[lson]+tree[rson];
}
void mark(int pos,int l,int r,int k)
{
tree[pos]+=(r-l+1)*k;
lazy[pos]+=k;
}
void pushdown(int pos,int l,int r)
{
int mid=(l+r)>>1;
mark(lson,l,mid,lazy[pos]);
mark(rson,mid+1,r,lazy[pos]);
lazy[pos]=0;
} void update(int pos,int l,int r,int x,int y,int k)
{
int mid=(l+r)>>1;
if(x<=l && r<=y)
{
mark(pos,l,r,k);
return;
}
pushdown(pos,l,r);
if(x<=mid)
update(lson,l,mid,x,y,k);
if(y>mid)
update(rson,mid+1,r,x,y,k);
tree[pos]=tree[lson]+tree[rson];
} void upd_chain(int x,int y,int k)
{
while(top[x]!=top[y])
{
if(deep[top[x]]<deep[top[y]])
swap(x,y);
update(1,1,n,id[top[x]],id[x],k);
x=fa[top[x]];
}
if(deep[x]<deep[y])
swap(x,y);
update(1,1,n,id[y]+1,id[x],k);
}
int query(int pos,int l,int r,int x,int y)
{
int ret=0;
int mid=(l+r)>>1;
if(x<=l && r<=y)
return tree[pos];
pushdown(pos,l,r);
if(x<=mid)
ret+=query(lson,l,mid,x,y);
if(y>mid)
ret+=query(rson,mid+1,r,x,y);
return ret;
}
int q_chain(int x,int y)
{
int ret=0;
while(top[x]!=top[y])
{
if(deep[top[x]]<deep[top[y]])
swap(x,y);
ret+=query(1,1,n,id[top[x]],id[x]);
x=fa[top[x]];
}
if(deep[x]<deep[y])
swap(x,y);
ret+=query(1,1,n,id[y]+1,id[x]);
return ret;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs1(1,0);
dfs2(1,1);
build(1,1,n);
while(m--)
{
char ch;
cin>>ch;
if(ch=='P')
{
int x,y;
scanf("%d%d",&x,&y);
upd_chain(x,y,1);
}
else
{
int x,y;
scanf("%d%d",&x,&y);
printf("%d\n",q_chain(x,y));
}
}
return 0;
}

USACO Grass Planting的更多相关文章

  1. spoj - Grass Planting(树链剖分模板题)

    Grass Planting 题意 给出一棵树,树有边权.每次给出节点 (u, v) ,有两种操作:1. 把 u 到 v 路径上所有边的权值加 1.2. 查询 u 到 v 的权值之和. 分析 如果这些 ...

  2. 洛谷P3038 [USACO11DEC]牧草种植Grass Planting

    题目描述 Farmer John has N barren pastures (2 <= N <= 100,000) connected by N-1 bidirectional road ...

  3. [USACO11DEC] Grass Planting (树链剖分)

    题目描述 Farmer John has N barren pastures (2 <= N <= 100,000) connected by N-1 bidirectional road ...

  4. AC日记——[USACO11DEC]牧草种植Grass Planting 洛谷 P3038

    题目描述 Farmer John has N barren pastures (2 <= N <= 100,000) connected by N-1 bidirectional road ...

  5. [Usaco2011 Dec]Grass Planting

    Description Farmer John has N barren pastures connected by N-1 bidirectional roads, such that there ...

  6. 洛谷 P3038 [USACO11DEC]牧草种植Grass Planting

    题目描述 Farmer John has N barren pastures (2 <= N <= 100,000) connected by N-1 bidirectional road ...

  7. P3038 [USACO11DEC]牧草种植Grass Planting

    题目描述 Farmer John has N barren pastures (2 <= N <= 100,000) connected by N-1 bidirectional road ...

  8. [USACO11DEC]Grass Planting

    题目大意: 有一棵结点个数为n的树,有m个操作,可以将一段路径上每条边的权值+1或询问某一个边的权值. 思路: 树链剖分+线段树. 轻重链划分本身比较简单,主要需要思考如何用线段树维护每条链. 当x, ...

  9. 【 SPOJ - GRASSPLA】 Grass Planting (树链剖分+树状数组)

    54  种草约翰有 N 个牧场,编号为 1 到 N.它们之间有 N − 1 条道路,每条道路连接两个牧场.通过这些道路,所有牧场都是连通的.刚开始的时候,所有道路都是光秃秃的,没有青草.约翰会在一些道 ...

随机推荐

  1. 8.Go-Reader,Writer和ioutil

    8.1.Reader (1)输入流 流是应用程序和外部资源进行数据交互的纽带 流分为输入流和输出流,输入和输出都是相对于程序,把外部数据传入程序中叫做输入流,反之叫做输出流 在Go语言标准库中io包下 ...

  2. 这一次,彻底弄懂 JavaScript 执行机制

    本文转自https://juejin.im/post/59e85eebf265da430d571f89#heading-4 本文的目的就是要保证你彻底弄懂javascript的执行机制,如果读完本文还 ...

  3. 【洛谷3515】[POI2011] Lightning Conductor(决策单调性)

    点此看题面 大致题意: 给你一个序列,对于每个\(i\)求最小的自然数\(p\)使得对于任意\(j\)满足\(a_j\le a_i+p-\sqrt{|i-j|}\). 证明单调性 考虑到\(\sqrt ...

  4. C语言——初见指针

    指针 指针是存放变量地址的变量. 由此我们可以知道了,指针存放的是变量地址且只能应用于内存中对象,指针本身也是一个变量.但是指针不仅仅可以存储变量地址,还可以是其他例如函数指针. C语言程序运行时,内 ...

  5. stm32串口烧写程序到开发板

    前言:我们如果要断电后重启板子还能使用程序的话就需要通过串口把程序烧写到开发板中. 1.设置Keil编译完之后,生成Hex文件.在工程选项[output]的标签,勾选“Create HEX File” ...

  6. vuex源码分析(二) state及strict属性 详解

    state也就是vuex里的值,也即是整个vuex的状态,而strict和state的设置有关,如果设置strict为true,那么不能直接修改state里的值,只能通过mutation来设置 例1: ...

  7. python asyncio 协程调用task步骤

    import asyncio async def compute(x, y): print("Compute %s + %s ..." % (x, y)) await asynci ...

  8. DevExpress的图形按钮菜单栏控件WindowsUIButtonPanel的布局、使用和设置按钮的点击事件

    场景 Winform控件-DevExpress18下载安装注册以及在VS中使用: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/1 ...

  9. Java生鲜电商平台-高并发的设计与架构

    Java生鲜电商平台-高并发的设计与架构 说明:源码下载Java开源生鲜电商平台以及高并发的设计与架构文档 对于高并发的场景来说,比如电商类,o2o,门户,等等互联网类的项目,缓存技术是Java项目中 ...

  10. 实战Rest API接口测试

    一.Android App API接口测试 1.如何学好Android App API接口测试 postman可以用来实现API接口自动化测试,但是也有弊端,无法实现接口测试数据的参数化,为了达到接口 ...