Codeforces191 C. Fools and Roads
传送门:>Here<
题意:给出一颗树,和K次操作。每次操作给出a,b,代表从a到b的路径上所有边的权值都+1(边权最开始全部为0)。最后依次输出每条边最终的权值
解题思路:
由于n非常大,不能暴力搞。于是就有Dalao提出了树链剖分……好像很有道理
然而,这是一道树上差分的经典题。于是就在这里介绍一下树上差分吧
再理解树上差分之前,先来看一看普通的差分:
给出一个全部为0的序列,每次操作给一段区间加上1,求最终序列中每个元素的值。
考虑差分——每一次操作$[L, R]$,令差分数组$cf[L]++$,$cf[R+1]--$。最后在统计的时候,我们从头开始扫描依次加上$cf$数组的值,就会依次得到每个元素的值。为什么这样是正确的呢?所谓差分,就是通过对头尾的操作,来完成整个区间的操作。如果差分数组$cf[x]$增加了$k$,就意味着从$x$开始到最后每个元素的值都要加上$k$。减法也是一个道理。因此我在结尾R+1处-1,相当于消除了差分对除此区间以外的元素的印象,因为前面的+1和后面的-1正好互相抵消了。因此最后在统计答案时,依次加上差分数组的值就代表了当前元素的值了。这个方法的应用范围是很广的,例如覆盖问题等等
理解了差分以后,树上差分也就是把差分放在了树上。当操作一次a到b之间的路径时,相当于$cf[a]++, cf[b]++, cf[lca(a, b)]-=2$. 此时我们的cf[i]的定义是从节点i到根节点的权值的前缀和,因此当我们操作a,b的路径时,相当于先把a到根的路径上+1,再把b到根的路径上+1,由于LCA到根的路径被重复加了两遍,因此减掉2. 统计的时候也和普通的差分一样,需要从前往后加起来得到当前边的经过次数。由于我们这里cf的数组时倒过来定义的,统计的时候也要从下往上走——在回溯的时候
还有一个问题,为什么要把cf的定义反过来呢?为什么不能再LCA的地方+1,两个端点-1呢,统计好像更方便啊?注意,这可不是一颗二叉树。当你在LCA处打一个差分标记的时候,它的意义是它之后的点的权值全部+1,这也就囊括了它的其他子树,而别的子树可能并没有被经历。这也给我们一个提示,当我们要在子树上差分时,可以这样差分。
Code
由于这道题是边权而不是点权,常见的做法是先把边权转化为点权,最后统计。很恶心的是这道题由于有边的编号,不得不使用链式前向星存图。而且是无向图,空间开两倍不能忘。a->b和b->a的边的编号恰好是$ (x^1)+2 $的关系。
/*by DennyQi*/
#include <cstdio>
#include <queue>
#include <algorithm>
#include <cstring>
#define r read()
#define Max(a,b) (((a)>(b))?(a):(b))
#define Min(a,b) (((a)<(b))?(a):(b))
using namespace std;
const int MAXN = ;
const int INF = 0x3f3f3f3f;
inline int read(){
int x = ; int w = ; register char c = getchar();
while(c ^ '-' && (c < '' || c > '')) c = getchar();
if(c == '-') w = -, c = getchar();
while(c >= '' && c <= '') x = (x<<) + (x<<) + c - '', c = getchar();
return x * w;
}
int N,x,y,num_edge,K;
int first[MAXN*],next[MAXN*],to[MAXN*],ans[MAXN*];
int dep[MAXN],f[MAXN][],cf[MAXN],val[MAXN];
inline void add(int u, int v){
to[++num_edge] = v;
next[num_edge] = first[u];
first[u] = num_edge;
}
void Dfs(int x, int father, int d){
dep[x] = d;
f[x][] = father;
for(int i = ; (<<i) <= d; ++i){
f[x][i] = f[f[x][i-]][i-];
}
int v;
for(int i = first[x]; i; i = next[i]){
v = to[i];
if(v == father) continue;
Dfs(v, x, d+);
}
}
inline int lca(int a, int b){
if(dep[a] < dep[b]) swap(a, b);
for(int i = ; i >= ; --i){
if((dep[a]-(<<i)) < dep[b]) continue;
a = f[a][i];
}
if(a == b) return a;
for(int i = ; i >= ; --i){
if(f[a][i] == f[b][i]) continue;
a = f[a][i];
b = f[b][i];
}
return f[a][];
}
void GetAns(int x, int father){
int v;
for(int i = first[x]; i; i = next[i]){
v = to[i];
if(v == father) continue;
GetAns(v, x);
val[x] += val[v];
ans[i] = val[v];
ans[(i^)+] = val[v];
}
val[x] += cf[x];
}
int main(){
// freopen(".in","r",stdin);
N=r;
for(int i = ; i < N; ++i){
x=r,y=r;
add(x, y);
add(y, x);
}
Dfs(, , );
K=r; int LCA;
while(K--){
x=r,y=r;
cf[x]++;
cf[y]++;
LCA = lca(x, y);
cf[LCA] -= ;
}
GetAns(, );
for(int i = ; i < N; ++i){
printf("%d ", ans[i<<]);
}
return ;
}
Codeforces191 C. Fools and Roads的更多相关文章
- CF 191C Fools and Roads lca 或者 树链剖分
They say that Berland has exactly two problems, fools and roads. Besides, Berland has n cities, popu ...
- Codeforces 191C Fools and Roads(树链拆分)
题目链接:Codeforces 191C Fools and Roads 题目大意:给定一个N节点的数.然后有M次操作,每次从u移动到v.问说每条边被移动过的次数. 解题思路:树链剖分维护边,用一个数 ...
- Fools and Roads CodeForces - 191C
Fools and Roads CodeForces - 191C 题意:给出一棵n个节点的树,还有树上的k条简单路径(用路径的两个端点u和v表示),对于树上每一条边,求出其被多少条简单路径经过. 方 ...
- CF191C Fools and Roads - 树剖解法
Codeforces Round #121 (Div. 1) C. Fools and Roads time limit per test :2 seconds memory limit per te ...
- [CF 191C]Fools and Roads[LCA Tarjan算法][LCA 与 RMQ问题的转化][LCA ST算法]
参考: 1. 郭华阳 - 算法合集之<RMQ与LCA问题>. 讲得很清楚! 2. http://www.cnblogs.com/lazycal/archive/2012/08/11/263 ...
- 【CF】121 Div.1 C. Fools and Roads
题意是给定一棵树.同时,给定如下k个查询: 给出任意两点u,v,对u到v的路径所经过的边进行加计数. k个查询后,分别输出各边的计数之和. 思路利用LCA,对cnt[u]++, cnt[v]++,并对 ...
- Codeforces 191 C Fools and Roads (树链拆分)
主题链接~~> 做题情绪:做了HDU 5044后就感觉非常easy了. 解题思路: 先树链剖分一下,把树剖分成链,由于最后全是询问,so~能够线性操作.经过树链剖分后,就会形成很多链,可是每条边 ...
- 题解 CF191C 【Fools and Roads】
树上差分半裸题 常规思路是进行三次DFS,然后常规运算即可 这里提供两次dfs的思路(wyz tql orz) 我们以样例2为例 我们考虑任意一条路径,令其起点为u终点为v,每走一次当前路径则v的访问 ...
- LCA+差分【CF191C】Fools and Roads
Description 有一颗 \(n\) 个节点的树,\(k\) 次旅行,问每一条边被走过的次数. Input 第一行一个整数 \(n\) (\(2\leq n\leq 10^5\)). 接下来 \ ...
随机推荐
- 04 Docker/基础设施 - DevOps之路
04 Docker/基础设施 - DevOps之路 文章Github地址,欢迎start:https://github.com/li-keli/DevOps-WiKi Docker是一个开源的引擎,可 ...
- 使用 Emmet 生成 HTML 的语法详解
生成 HTML 文档初始结构 HTML 文档的初始结构,就是包括 doctype.html.head.body 以及 meta 等内容.你只需要输入一个 “!” 就可以生成一个 HTML5 的标准文档 ...
- codeforces#1090 D. New Year and the Permutation Concatenation(打表找规律)
题意:给出一个n,生成n的所有全排列,将他们按顺序前后拼接在一起组成一个新的序列,问有多少个长度为n的连续的子序列和为(n+1)*n/2 题解:由于只有一个输入,第一感觉就是打表找规律,虽然表打出来了 ...
- 快速失败/报错机制 - fail-fast
一.快速报错机制(fail-fast) 这是<Java编程思想>中关于快速报错机制的描述 Java容器有一种保护机制,能够防止多个进程同时修改同一个容器的内容.如果在你迭代遍历容器的过程中 ...
- 百度地图开发者API学习笔记一(转载)
一,实现功能: 在地图上标记点,划线等操作.如下图. 2.代码: <!DOCTYPE html> <html> <head> <meta http-equiv ...
- mybatis之批量插入
一.导入功能优化 普通for循环,对于导入大量数据时非常耗时.可以通过Mybatis的批量插入功能提高效率.每批次导入的数据不能太多,否则会报错.通过测试发现,每批次200条为宜. 测试结果: 开启事 ...
- CodeIgniter框架中尝试使用swoole
ci框架版本:3.1.7. swoole版本:1.7. php版本:5.6 相关文档: 以cli方式运行ci框架 swoole官方手册 创建一个TestSwoole和Hello控制器 ...
- jmeter压测参数设定(转)
jmeter压测参数设定 一.基本公式 线程数 = QPS * time: 注:QPS--每秒完成请求的个数:time--每个请求响应完成平均需要时间: 故QPS * time就是所有请求完成响应所需 ...
- Tomcat 目录结构以及基本配置
1 Tomcat 目录层次结构 ① bin:存放启动和关闭tomcat 的脚本文件② conf: 存放配置文件 server.xml:该文件用于配置和server 相关的信息,比如tomcat 启动端 ...
- Oracle 表空间的创建与管理
Oracle数据库创建之后有一些默认的表空间随之被创建,查询数据字典 dba_data_files 可以得到数据库当前的所有表空间信息. select * from v$tablespace; sel ...