洛谷 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. 微信小程序-自定义方法的抛出与引用

    一. 定义方法与抛出(utils/foo.js文件中) function say () { console.log('自定义的say方法')}  # 定义方法 module.exports = {sa ...

  2. phpize安装PHP扩展

    安装编译完成php源码后忘记安装一些扩展可以通过phpize来安装 拿lnmp1.6安装举例 安装完成lnmp后发现有些扩展没有 lnmp1.6的安装脚本会在lnmp1.6里生成src,里面是lnmp ...

  3. 1+X证书Web前端开发规范手册

    一.规范目的 1.1 概述 为提高团队协作效率, 便于后台人员添加功能及前端后期优化维护, 输出高质量的文档, 特制订此文档. 本规范文档一经确认, 前端开发人员必须按本文档规范进行前台页面开发. 本 ...

  4. PDF文件添加二维码水印教程

    maven配置iText的jar,主要不是所有私服都有iText的jar,maven仓库没有的,可以去https://mvnrepository.com/artifact/com.itextpdf/i ...

  5. Shell基本运算符之布尔运算符、逻辑运算符

    Shell基本运算符 =============================摘自与菜鸟教程=============================== 1.布尔运算符 ! 非运算,表达式为tru ...

  6. .NET Core 多框架支持(net45+netstandard20)实践中遇到的一些问题总结

    .NET Core 多框架支持(net45+netstandard20)实践中遇到的一些问题总结 前言 本文主要是关于.NET Standard 代码 在多框架 和 多平台 支持自己实践过程中遇到的一 ...

  7. vue-cli安装以及创建一个简单的项目(一)(Node\npm\webpack简单使用)

    1.关系介绍 1.简单的说 Node.js 就是运行在服务端的 JavaScript. 2.NPM是随同NodeJS一起安装的包管理工具(新版的nodejs已经集成了npm),能解决NodeJS代码部 ...

  8. 一些优秀的 Entity Framework 开源项目

    增加全局过滤的项目(比如:IsDeleted 字段):https://github.com/zzzprojects/EntityFramework.DynamicFilters 谢谢浏览!

  9. 如何使用gitlab自建golang基础库

    这里以go mod方式建立golang基础库 一.gitlab创建项目golib 地址为gitlab.xxx.com/base/golib 示例如下 go mod初始化命令 go mod init g ...

  10. OpenGL入门1.3:着色器 GLSL

    前言 经过之前一段时间的学习(渲染管线简介)我们已经知道了着色器(Shader)是运行在GPU上的程序,这些小程序为图形渲染管线的某个特定部分而运行,着色器只是一种把输入转化为输出的程序,着色器也是一 ...