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 条道路,每条道路连接两个牧场.通过这些道路,所有牧场都是连通的.刚开始的时候,所有道路都是光秃秃的,没有青草.约翰会在一些道 ...
随机推荐
- matlab练习程序(贝塞尔曲线)
下面三个公式分别是一次.二次和三次贝塞尔曲线公式: 通用的贝塞尔曲线公式如下: 可以看出,系数是由一个杨辉三角组成的. 这里的一次或者二次三次由控制点个数来决定,次数等于控制点个数-1. 实现的效果如 ...
- celery分布式任务队列的使用
一.Celery介绍和基本使用 Celery 是一个 基于python开发的分布式异步消息任务队列,通过它可以轻松的实现任务的异步处理, 如果你的业务场景中需要用到异步任务,就可以考虑使用celery ...
- 解惑:如何使用SecureCRT上传和下载文件、SecureFX乱码问题
解惑:如何使用SecureCRT上传和下载文件.SecureFX乱码问题 一.前言 很多时候在windows平台上访问Linux系统的比较好用的工具之一就是SecureCRT了,下面介绍一下这个软件的 ...
- 在 React 组件中监听 android 手机物理返回/回退/back键事件
当前端页面嵌入到 webview 中运行时,有时会需要监听手机的物理返回按键事件来做一些自定义的操作. 比如我最近遇到的,在一个页面里面有批量选择的功能,当点击手机的返回键时,清除页面上的选中状态.我 ...
- HTML连载18-id选择器与class区别&class选择器使用思路&后代选择器
一.id选择器和classable选择器的区别 选择器 CSS中的开头 HTML标签可以绑定几个 是否可重复 用途 id选择器 # 仅能一个 不可以重复(一个标签里仅有一个) 一般情况下是给JS用的, ...
- Centos7安装percona-xtrabackup2.4和8.0版本
Percona XtraBackup是一个基于MySQL的服务器的开源热备份实用程序 ,它不会在备份期间锁定您的数据库.无论是24x7高负载服务器还是低事务量环境,Percona XtraBackup ...
- python学习笔记 - socket通信
socket socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. sock ...
- webstorm关闭烦人的eslint语法检查
使用了eslint语法检查之后发现JS代码里面处处是红线,通过右键菜单中的fix eslint problems选项又会发现页面代码的格式被eslint换行得不分青红皂白,索性关闭exlint语法检查 ...
- IDEA 日常小技巧
原文首发于 studyidea.cn点击查看更多技巧 适用于 IDEA 2019.2 之前版本 ,2019.2 版本以下功能默认开启. Surround a selection with a quot ...
- Could not find resource——mybatis 找不到映射器xml文件
今天用IDEA写Mybatis的时候,测试报了如图所示的错,恶心死我了,后来解决了,总结一下,防止下回跳坑,当然,也是做一个分享,如果有朋友遇到这个错,希望有所帮助 Error parsing SQL ...