USACO Grass Planting
洛谷 P3038 [USACO11DEC]牧草种植Grass Planting
JDOJ 2282: USACO 2011 Dec Gold 3.Grass Planting
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
题目大意:
给定一个有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的更多相关文章
- spoj - Grass Planting(树链剖分模板题)
Grass Planting 题意 给出一棵树,树有边权.每次给出节点 (u, v) ,有两种操作:1. 把 u 到 v 路径上所有边的权值加 1.2. 查询 u 到 v 的权值之和. 分析 如果这些 ...
- 洛谷P3038 [USACO11DEC]牧草种植Grass Planting
题目描述 Farmer John has N barren pastures (2 <= N <= 100,000) connected by N-1 bidirectional road ...
- [USACO11DEC] Grass Planting (树链剖分)
题目描述 Farmer John has N barren pastures (2 <= N <= 100,000) connected by N-1 bidirectional road ...
- AC日记——[USACO11DEC]牧草种植Grass Planting 洛谷 P3038
题目描述 Farmer John has N barren pastures (2 <= N <= 100,000) connected by N-1 bidirectional road ...
- [Usaco2011 Dec]Grass Planting
Description Farmer John has N barren pastures connected by N-1 bidirectional roads, such that there ...
- 洛谷 P3038 [USACO11DEC]牧草种植Grass Planting
题目描述 Farmer John has N barren pastures (2 <= N <= 100,000) connected by N-1 bidirectional road ...
- P3038 [USACO11DEC]牧草种植Grass Planting
题目描述 Farmer John has N barren pastures (2 <= N <= 100,000) connected by N-1 bidirectional road ...
- [USACO11DEC]Grass Planting
题目大意: 有一棵结点个数为n的树,有m个操作,可以将一段路径上每条边的权值+1或询问某一个边的权值. 思路: 树链剖分+线段树. 轻重链划分本身比较简单,主要需要思考如何用线段树维护每条链. 当x, ...
- 【 SPOJ - GRASSPLA】 Grass Planting (树链剖分+树状数组)
54 种草约翰有 N 个牧场,编号为 1 到 N.它们之间有 N − 1 条道路,每条道路连接两个牧场.通过这些道路,所有牧场都是连通的.刚开始的时候,所有道路都是光秃秃的,没有青草.约翰会在一些道 ...
随机推荐
- goroutine使用
Goroutine是建立在线程之上的轻量级的抽象.它允许我们以非常低的代价在同一个地址空间中并行地执行多个函数或者方法.相比于线程,它的创建和销毁的代价要小很多,并且它的调度是独立于线程的.在gola ...
- java基础 Unsafe
参考文章: https://tech.meituan.com/2019/02/14/talk-about-java-magic-class-unsafe.html
- github上方便的小工具
目录 python中的fire模块 Install Reference python中的fire模块 它可以对所有Python 对象,包括functions, classes, modules, ob ...
- ubuntu / zsh shell / oh-my-zsh / 常用插件
记录一下 zsh 的下载与配置,省得每次重装系统都要上网到处查. 安装 zsh shell sudo apt install zsh 切换 shell chsh -s /bin/zsh 安装 oh-m ...
- 【linux】glibc升级
glibc升级 步骤如下: 1.下载解压glibc wget http://ftp.gnu.org/gnu/glibc/glibc-2.18.tar.gz tar zxvf glibc-2.18.ta ...
- 『珂朵莉树 Old Driver Tree』
珂朵莉树 珂朵莉树其实不是树,只是一个借助平衡树实现的数据结构,主要是对于有区间赋值的数据结构题,可以用很暴力的代码很高效地完成任务,当然这是建立在数据随机的基础上的. 即使数据不是随机的,写一个珂朵 ...
- linq,skip(),take实现分页
using (AdventureWorks2012Entities db = new AdventureWorks2012Entities()) { int num = (from stu in db ...
- ADO.NET中的5个主要对象
1.Connection:主要是开启程序和数据库之间的连接.没有利用连接对象将数据库打开,是无法从数据库中取得数据的. Close和Dispose的区别,Close以后还可以Open,Dispose以 ...
- .Net常见的IOC框架及AOP框架
IOC框架 Unity:微软patterns&practicest团队开发的IOC依赖注入框架,支持AOP横切关注点. MEF(Managed Extensibility Framework) ...
- jvm常用排错命令
jvm命令很多,有一篇博客整理的非常全 https://www.cnblogs.com/ityouknow/p/5714703.html.我只列举一些常用的排错用到的. jps -l -v ...