Codeforces 838B - Diverging Directions - [DFS序+线段树]
题目链接:http://codeforces.com/problemset/problem/838/B
You are given a directed weighted graph with n nodes and 2n - 2 edges. The nodes are labeled from 1 to n, while the edges are labeled from 1 to 2n - 2. The graph's edges can be split into two parts.
- The first n - 1 edges will form a rooted spanning tree, with node 1 as the root. All these edges will point away from the root.
- The last n - 1 edges will be from node i to node 1, for all 2 ≤ i ≤ n.
You are given q queries. There are two types of queries
- 1 i w: Change the weight of the i-th edge to w
- 2 u v: Print the length of the shortest path between nodes u to v
Given these queries, print the shortest path lengths.
Input
The first line of input will contain two integers n, q (2 ≤ n, q ≤ 200 000), the number of nodes, and the number of queries, respectively.
The next 2n - 2 integers will contain 3 integers ai, bi, ci, denoting a directed edge from node ai to node bi with weight ci.
The first n - 1 of these lines will describe a rooted spanning tree pointing away from node 1, while the last n - 1 of these lines will have bi = 1.
More specifically,
- The edges (a1, b1), (a2, b2), ... (an - 1, bn - 1) will describe a rooted spanning tree pointing away from node 1.
- bj = 1 for n ≤ j ≤ 2n - 2.
- an, an + 1, ..., a2n - 2 will be distinct and between 2 and n.
The next q lines will contain 3 integers, describing a query in the format described in the statement.
All edge weights will be between 1 and 106.
Output
For each type 2 query, print the length of the shortest path in its own line.
Example
5 9
1 3 1
3 2 2
1 4 3
3 5 4
5 1 5
3 1 6
2 1 7
4 1 8
2 1 1
2 1 3
2 3 5
2 5 2
1 1 100
2 1 3
1 8 30
2 4 2
2 2 4
0
1
4
8
100
132
10
题意:
给出n个节点,2n-2条边(有向带权);
其中前n-1条边使得n个点构成一棵带权有向树;后n-1条边,权重为w,是从2~n号节点直接连接向1号节点的;
现在给出两种操作:
①修改第 i 条边的权重为w;
②查询节点u和v之间最短路径的权重和;
题解:
先忽略后n-1条边,DFS序拍平整棵树;
同时在DFS时,顺便计算出节点i的dist[i],代表从节点1到节点i的唯一的一条路径的权重和;
记录每个节点的 dist[i] + Edge(i→1).weight,用线段树在DFS序上维护区间最小值;
then……
对于修改操作:
①若修改的边是Edge(x→1)类型的,那么更新区间[ in[x] , in[x] ]上的值;
②否则,更新区间[ in[x] , out[x] ]上的值;
对于查询操作:
①v在u统领的子树内,按道理来讲直接dist[v]-dist[u]即可,
但是我们在修改边权时不对dist[]数组进行更新,所以要通过式子dist[x] = query([ in[x] , in[x] ]) - Edge(x→1).weight来求得dist[u]和dist[v];
②v不在u统领的子树内,那么只能通过 u → … → 1 → … → v 这样的路径从u走到v,
显然,u统领的子树内的每个节点,都能使得从节点u出发回到节点1,
那么我们就query([ in[u] , out[u] ])查:u统领的这颗子树内的哪个节点,它的 dist[x] + Edge(x→1).weight 是最小的;
一旦查到,ans = query([ in[u] , out[u] ]) - dist[u] + dist[v],同样的道理,这里的dist[u]和dist[v]都要像上面那样通过式子 dist[x] = query([ in[x] , in[x] ]) - Edge(x→1).weight 求得。
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=+;
const LL INF=1e18; int n,q; //邻接表
struct Edge{
int u,v;
LL w;
Edge(int u,int v,LL w)
{
this->u=u;
this->v=v;
this->w=w;
}
};
vector<Edge> E; int E_size;
vector<int> G[maxn];
int ptr[*maxn];
void adjListInit(int l,int r)
{
E.clear(); E_size=;
for(int i=l;i<=r;i++) G[i].clear();
}
void addEdge(int u,int v,LL w,int i)
{
E.push_back(Edge(u,v,w)); E_size++;
ptr[i]=E_size-;
G[u].push_back(E_size-);
} //存储所有返回到1的边(即编号为n~2n-2的边)
vector<Edge> Eback;
int Gback[maxn];
int Eback_size; //DFS建立DFS序列,以及计算深度
LL dist[maxn];
int in[maxn],out[maxn];
int peg[maxn];
int dfs_clock;
inline void dfsInit()
{
dist[]=;
dfs_clock=;
}
void dfs(int now,int par)
{
//printf("now=%d\n",now);
in[now]=++dfs_clock;
peg[in[now]]=now; for(int i=,_size=G[now].size();i<_size;i++)
{
Edge &e=E[G[now][i]]; int nxt=e.v;
if(nxt!=par)
{
dist[nxt]=dist[now]+e.w;
dfs(nxt,now);
}
} out[now]=dfs_clock;
} //线段树
struct Node{
int l,r;
LL val,lazy;
void update(LL x)
{
val+=x;
lazy+=x;
}
}node[*maxn];
void pushdown(int root)
{
if(node[root].lazy)
{
node[root*].update(node[root].lazy);
node[root*+].update(node[root].lazy);
node[root].lazy=;
}
}
void pushup(int root)
{
node[root].val=min(node[root*].val,node[root*+].val);
}
void build(int root,int l,int r)
{
node[root].l=l; node[root].r=r;
node[root].val=; node[root].lazy=;
if(l==r)
{
int p=peg[l]; //从DFS序中的位置逆向查节点编号
node[root].val=dist[p]+Eback[Gback[p]].w;
}
else
{
int mid=l+(r-l)/;
build(root*,l,mid);
build(root*+,mid+,r);
pushup(root);
}
}
void update(int root,int st,int ed,int val)
{
if(st>node[root].r || ed<node[root].l) return;
if(st<=node[root].l && node[root].r<=ed) node[root].update(val);
else
{
pushdown(root);
update(root*,st,ed,val);
update(root*+,st,ed,val);
pushup(root);
}
}
LL query(int root,int st,int ed)
{
if(ed<node[root].l || node[root].r<st) return INF;
if(st<=node[root].l && node[root].r<=ed) return node[root].val;
else
{
pushdown(root);
LL lson=query(root*,st,ed);
LL rson=query(root*+,st,ed);
pushup(root);
return min(lson,rson);
}
} int main()
{
cin>>n>>q; adjListInit(,n);
for(int i=;i<=n-;i++)
{
int a,b; LL c;
scanf("%d%d%I64d",&a,&b,&c);
addEdge(a,b,c,i);
} dfsInit();
dfs(,-);
//for(int i=1;i<=n;i++) printf("in[%d]=%d out[%d]=%d\n",i,in[i],i,out[i]); Eback.clear(); Eback_size=;
for(int i=n,a,b,c;i<=*n-;i++)
{
scanf("%d%d%d",&a,&b,&c);
Eback.push_back(Edge(a,b,c)); Eback_size++;
ptr[i]=Eback_size-;
Gback[a]=Eback_size-;
}
Eback.push_back(Edge(,,)); Eback_size++;
Gback[]=Eback_size-; build(,,n);
for(int i=,type;i<=q;i++)
{
scanf("%d",&type);
if(type==)
{
int id; LL w;
scanf("%d%I64d",&id,&w);
if(id<n)
{
Edge &e=E[ptr[id]];
//printf("Edge %d: %d->%d = %I64d\n",id,e.u,e.v,e.w);
update(,in[e.v],out[e.v],w-e.w);
e.w=w;
}
else
{
Edge &e=Eback[ptr[id]];
//printf("Edge %d: %d->%d = %I64d\n",id,e.u,e.v,e.w);
update(,in[e.u],in[e.u],w-e.w);
e.w=w;
}
}
if(type==)
{
int u,v;
scanf("%d%d",&u,&v);
if(in[u]<=in[v] && in[v]<=out[u]) //v在u统领的子树内
{
//printf("%d是%d的祖先\n",u,v);
LL du=query(,in[u],in[u])-Eback[Gback[u]].w;
LL dv=query(,in[v],in[v])-Eback[Gback[v]].w;
printf("%I64d\n",dv-du);
}
else
{
//printf("%d不是%d的祖先\n",u,v);
LL ans=query(,in[u],out[u]); LL du=query(,in[u],in[u])-Eback[Gback[u]].w;
LL dv=query(,in[v],in[v])-Eback[Gback[v]].w;
ans-=du;
ans+=dv; printf("%I64d\n",ans);
}
}
}
}
PS.第一次200+行的代码1A,还是值得小小窃喜一下的
Codeforces 838B - Diverging Directions - [DFS序+线段树]的更多相关文章
- Educational Codeforces Round 6 E dfs序+线段树
题意:给出一颗有根树的构造和一开始每个点的颜色 有两种操作 1 : 给定点的子树群体涂色 2 : 求给定点的子树中有多少种颜色 比较容易想到dfs序+线段树去做 dfs序是很久以前看的bilibili ...
- Codeforces Round #442 (Div. 2)A,B,C,D,E(STL,dp,贪心,bfs,dfs序+线段树)
A. Alex and broken contest time limit per test 2 seconds memory limit per test 256 megabytes input s ...
- CodeForces 877E DFS序+线段树
CodeForces 877E DFS序+线段树 题意 就是树上有n个点,然后每个点都有一盏灯,给出初始的状态,1表示亮,0表示不亮,然后有两种操作,第一种是get x,表示你需要输出x的子树和x本身 ...
- Codeforces 343D Water Tree(DFS序 + 线段树)
题目大概说给一棵树,进行以下3个操作:把某结点为根的子树中各个结点值设为1.把某结点以及其各个祖先值设为0.询问某结点的值. 对于第一个操作就是经典的DFS序+线段树了.而对于第二个操作,考虑再维护一 ...
- CodeForces 877E Danil and a Part-time Job(dfs序+线段树)
Danil decided to earn some money, so he had found a part-time job. The interview have went well, so ...
- 【BZOJ-3252】攻略 DFS序 + 线段树 + 贪心
3252: 攻略 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 339 Solved: 130[Submit][Status][Discuss] D ...
- BZOJ2434 [Noi2011]阿狸的打字机(AC自动机 + fail树 + DFS序 + 线段树)
题目这么说的: 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母.经阿狸研究发现,这个打字机是这样工作的: 输入小 ...
- POJ 3321 DFS序+线段树
单点修改树中某个节点,查询子树的性质.DFS序 子树序列一定在父节点的DFS序列之内,所以可以用线段树维护. 1: /* 2: DFS序 +线段树 3: */ 4: 5: #include < ...
- 【XSY2667】摧毁图状树 贪心 堆 DFS序 线段树
题目大意 给你一棵有根树,有\(n\)个点.还有一个参数\(k\).你每次要删除一条长度为\(k\)(\(k\)个点)的祖先-后代链,问你最少几次删完.现在有\(q\)个询问,每次给你一个\(k\), ...
随机推荐
- SVN的Hooks功能--强制添加注释
所谓hooks,可以类似 理解Linux内核Netfilter框架的hook点和hook函数的概念.当用户在维护代码的过程中,其执行的相关动作正好触发了相关hook点,就 会去执行对应hook点的脚本 ...
- JAVA自定义注解 和 运行时靠 反射获取注解,解决 shiro 注解型权限因子获取问题
项目的权限分配,采用的是RBAC的设计模式.后台配置权限的时候,需要获取到所有的权限因子. 不经让我想起YII框架的SRBAC模块,还有以前的一个ecshop改造系统的权限配置方式,都采用的是PHP的 ...
- Maven 多项目依赖,需要验证artifact的output root中是否包含其他项目输出
- 3. Oracle数据库逻辑备份与恢复
一. Oracle逻辑备份介绍 Oracle逻辑备份的核心就是复制数据:Oracle提供的逻辑备份与恢复的命令有exp/imp,expdp/impdp.当然像表级复制(create table tab ...
- 据库分库分表(sharding)系列(一) 拆分实施策略和示例演示
本文原文连接: http://blog.csdn.net/bluishglc/article/details/7696085 ,转载请注明出处!本文着重介绍sharding切分策略,如果你对数据库sh ...
- php curl那点事儿
curl是最常用功能之一初始化句柄 $ch = curl_init(); post 传$data 1. 如果$data是字符串,则Content-Type是application/x-www-form ...
- 【Java并发编程二】同步容器和并发容器
一.同步容器 在Java中,同步容器包括两个部分,一个是vector和HashTable,查看vector.HashTable的实现代码,可以看到这些容器实现线程安全的方式就是将它们的状态封装起来,并 ...
- 【ipad神坑】ipad麦克风听不到声音怎么回事 微信QQ语音视频对方都听不到
今天遇到了这个问题 说话听不见,但是敲击ipad,可以明显的听到击打的声音 siri也是可以听到 上网上找,大多都是说恢复设置,重启,隐私麦克风权限等解决方案 都是浪费感情 全部尝试过了,依然没有用. ...
- java高级---->Thread之Phaser的使用
Phaser提供了动态增parties计数,这点比CyclicBarrier类操作parties更加方便.它是jdk1.7新增的类,今天我们就来学习一下它的用法.尘埃落定之后,回忆别来挑拨. Phas ...
- 深入理解 Neutron -- OpenStack 网络实现(2):VLAN 模式
问题导读 1.br-int.br-ethx的作用是什么?2.安全组策略是如何实现的?3.VLAN 模式与GRE模式有哪些不同点?流量上有哪些不同?4.L3 agent实现了什么功能? 接上篇深入理解 ...