传送门:http://acm.hdu.edu.cn/showproblem.php?pid=5957

题意:D(u,v)是节点u和节点v之间的距离,S(u,v)是一系列满足D(u,x)<=k的点的集合,操作1:将S(u,k)内节点权值增加或者减小,操作2:查询S(u,k)内节点的权值和

题解:因为题目说了查询和更新的距离小于等于k,k最大为2,所以很显然要分情况讨论k为0、1、2的情况

因为是多次更新,我们显然是需要用线段树来维护节点权值的

运用线段树和bfs序的知识我们知道

对一个棵树求BFS序后,深度相同的节点的序号是相邻的。
对于节点u,如果知道它儿子的最小BFS序号L和最大BFS序号R,那么它儿子的所有序号就在[L,R]中。

这样就比较方便对区间进行查询或者修改操作

根据题意可以知道:每次更新的时候

如果k==0,那么就只更新自己

如果k==1,那么就更新自己还有和自己相连的边,由于存在环的情况,所以我们要首先处理每个节点的入度,处理完入度的话,如果这个点的入度是1,那么证明这个点就不在环上,就更新他自己,他的儿子,他的父亲节点即可,如果这个点的入度大于1,那么这个点就在环上,稍微画个图就知道,环上就有左爸爸和右爸爸,将这两个节点给更新就好

如果k==2,那么情况就比较复杂了,首先是要更新自己,然后,和自己相连的边,和之前一样要判断环的情况,没有环的话,再讨论自己的爸爸节点还有儿子节点的情况,可能存在爸爸节点在环上、爸爸节点不在环上,儿子节点在环上、儿子节点不在环上,这样分类讨论完后即可

求和和更新差不多就不多讲了

代码有注释.

代码如下:

#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <stack>
#include <queue>
#include <cstdio>
#include <cctype>
#include <bitset>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
#define PI acos(-1)
#define eps 1e-8
#define fuck(x) cout<<#x<<" = "<<x<<endl;
#define FIN freopen("input.txt","r",stdin);
#define FOUT freopen("output.txt","w+",stdout);
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int maxn = 2e5 + ;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + ;
LL gcd(LL a, LL b) {return b ? gcd(b, a % b) : a;}
LL lcm(LL a, LL b) {return a / gcd(a, b) * b;}
LL powmod(LL a, LL b, LL MOD) {LL ans = ; while (b) {if (b % )ans = ans * a % MOD; a = a * a % MOD; b /= ;} return ans;}
double dpow(double a, LL b) {double ans = 1.0; while (b) {if (b % )ans = ans * a; a = a * a; b /= ;} return ans;}
int n, q;
struct node {
int v, nxt, w;
} edge[maxn];
int head[maxn];
int tot;
void add_edge(int u, int v) {
edge[tot].v = v;
edge[tot].nxt = head[u];
head[u] = tot++;
} int in[maxn]; void top() {
queue<int>q;
for (int i = ; i <= n; i++) if (in[i] == ) q.push(i);
while (!q.empty()) {
int u = q.front(); q.pop();
for (int i = head[u]; ~i; i = edge[i].nxt) {
int v = edge[i].v;
if (in[v] > ) {
in[v]--;
if (in[v] == ) q.push(v);
}
}
}
} LL sum[maxn << ], add[maxn << ];
void build(int l, int r, int rt) {
sum[rt] = add[rt] = ;
if (l == r) return;
int m = (l + r) >> ;
build(lson);
build(rson);
}
void PushDown(int m, int rt) {
if (add[rt]) {
add[rt << ] += add[rt];
add[rt << | ] += add[rt];
sum[rt << ] += add[rt] * (m - (m >> ));
sum[rt << | ] += add[rt] * (m >> );
add[rt] = ;
}
}
void PushUP(int rt) {
sum[rt] = sum[rt << ] + sum[rt << | ];
}
void update(int L, int R, int c, int l, int r, int rt) {
if (L == || R == ) return;
if (L <= l && R >= r) {
sum[rt] += (LL)(r - l + ) * c;
add[rt] += c;
return;
}
PushDown(r - l + , rt);
int m = (l + r) >> ;
if (L <= m) update(L, R, c, lson);
if (R > m) update(L, R, c, rson);
PushUP(rt);
}
LL query(int L, int R, int l, int r, int rt) {
if (L == || R == ) return ;
if (L <= l && R >= r) return sum[rt];
PushDown(r - l + , rt);
int m = (l + r) >> ;
LL ret = ;
if (L <= m) ret += query(L, R, lson);
if (R > m) ret += query(L, R, rson);
PushUP(rt);
return ret;
}
int ver[maxn][]; int p[maxn], fp[maxn], fa[maxn], sz;
int sonL[maxn], sonR[maxn], ssonL[maxn], ssonR[maxn];
//设val[u]为节点u的权值,fa[u]为父亲,son[u]为儿子,sson[u]孙子
void bfs(int top) { //bfs序找最左和最右的区间
queue<int>q;
q.push(top);
while (!q.empty()) {
int u = q.front(); q.pop();
sonL[u] = ssonL[u] = INF;
sonR[u] = ssonR[u] = ;
for (int i = head[u]; i!=-; i = edge[i].nxt) {
int v = edge[i].v;
if (in[v] > || v == fa[u]) continue; //如果连的点在环上就不管
p[v] = ++sz; //继续打编号
fp[sz] = v;
fa[v] = u;
sonL[u] = min(sonL[u], p[v]);
sonR[u] = max(sonR[u], p[v]);
q.push(v);
}
ssonL[fa[u]] = min(ssonL[fa[u]], sonL[u]);
ssonR[fa[u]] = max(ssonR[fa[u]], sonR[u]);
}
} void change(int u, int k, int d) {
int father = fa[u];
//分类讨论
//如果k==0,那么就更新单个节点
//如果k==1,那么就更新u的儿子和爸爸还有自己
//1)需要讨论是否在环上,环上的话相当于有两个爸爸
//如果k==2,那么就更新u的儿子和孙子,u的爸爸和u的爷爷,还有自己
//1)需要讨论u是否在环上,环上的话,有两个爸爸和两个爷爷都要更新
//2)需要讨论u的儿子和孙子是否在环上
if (k == ) update(p[u], p[u], d, , n, );
else if (k == ) {
update(sonL[u], sonR[u], d, , n, );
update(p[u], p[u], d, , n, );
if (in[u] == ) update(p[fa[u]], p[fa[u]], d, , n, );
else {
update(p[ver[u][]], p[ver[u][]], d, , n, );
update(p[ver[u][]], p[ver[u][]], d, , n, );
}
} else if (k == ) {
//update(p[u],p[u],d,1,n,1);
update(sonL[u], sonR[u], d, , n, );
update(ssonL[u], ssonR[u], d, , n, );
if (in[u] == ) {
update(p[fa[u]], p[fa[u]], d, , n, );
update(sonL[fa[u]], sonR[fa[u]], d, , n, );
if (in[fa[u]] == ) {
update(p[fa[fa[u]]], p[fa[fa[u]]], d, , n, );
} else {
update(p[ver[fa[u]][]], p[ver[fa[u]][]], d, , n, );
update(p[ver[fa[u]][]], p[ver[fa[u]][]], d, , n, );
}
} else {
update(p[u], p[u], d, , n, );
int vv[];
int cnt = ;
for (int i = ; i < ; i++) {
int v = ver[u][i];
update(p[v], p[v], d, , n, );
update(sonL[v], sonR[v], d, , n, );
for (int j = ; j < ; j++) {
if (ver[v][j] == u || ver[v][j] == ver[u][] || ver[v][j] == ver[u][]) continue;
if (cnt > && ver[v][j] == vv[cnt - ]) continue;
vv[cnt++] = ver[v][j]; }
}
for (int i = ; i < cnt; i++) {
update(p[vv[i]], p[vv[i]], d, , n, );
}
}
}
}
int get_ans(int u, int k) {
//分类讨论
//如果k==0,那么就查询单个节点
//如果k==1,那么就查询u的儿子和爸爸还有自己
//1)需要讨论是否在环上,环上的话相当于有两个爸爸
//如果k==2,那么就查询u的儿子和孙子,u的爸爸和u的爷爷,还有自己
//1)需要讨论u是否在环上,环上的话,有两个爸爸和两个爷爷都要查询
//2)需要讨论u的儿子和孙子是否在环上
int ans = ;
if (k == ) ans += query(p[u], p[u], , n, );
else if (k == ) {
ans += query(sonL[u], sonR[u], , n, );
ans += query(p[u], p[u], , n, );
if (in[u] == ) ans += query(p[fa[u]], p[fa[u]], , n, );
else {
ans += query(p[ver[u][]], p[ver[u][]], , n, );
ans += query(p[ver[u][]], p[ver[u][]], , n, );
}
} else if (k == ) {
//update(p[u],p[u],d,1,n,1);
ans += query(sonL[u], sonR[u], , n, );
ans += query(ssonL[u], ssonR[u], , n, );
if (in[u] == ) {
ans += query(p[fa[u]], p[fa[u]], , n, );
ans += query(sonL[fa[u]], sonR[fa[u]], , n, );
if (in[fa[u]] == ) {
ans += query(p[fa[fa[u]]], p[fa[fa[u]]], , n, );
} else {
ans += query(p[ver[fa[u]][]], p[ver[fa[u]][]], , n, );
ans += query(p[ver[fa[u]][]], p[ver[fa[u]][]], , n, );
}
} else {
ans += query(p[u], p[u], , n, );
int vv[];
int cnt = ;
for (int i = ; i < ; i++) {
int v = ver[u][i];
ans += query(p[v], p[v], , n, );
ans += query(sonL[v], sonR[v], , n, );
for (int j = ; j < ; j++) {
if (ver[v][j] == u || ver[v][j] == ver[u][] || ver[v][j] == ver[u][]) continue;
if (cnt > && ver[v][j] == vv[cnt - ]) continue;
vv[cnt++] = ver[v][j];
}
}
for (int i = ; i < cnt; i++) {
ans += query(p[vv[i]], p[vv[i]], , n, );
}
}
}
return ans;
}
int main() {
#ifndef ONLINE_JUDGE
FIN
#endif
int T;
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
for (int i = ; i <= n; i++) {
in[i] = ; fa[i] = ; head[i] = -;
}
tot = ;
sz=;
int u, v;
for (int i = ; i <= n; i++) {
scanf("%d%d", &u, &v);
in[v]++;
in[u]++;
add_edge(u, v);
add_edge(v, u);
}
top();
for (int u = ; u <= n; u++) {//遍历每一个节点
if (in[u] > ) {//如果节点的入度大于1
int j = ;
for (int i = head[u]; i!=-; i = edge[i].nxt) { //这个节点所有的子树
int v = edge[i].v;
if (in[v] > ) {//如果这个点在环上
ver[u][j++] = v;//记录点u在环上连的是哪两个点
}
}
p[u] = ++sz;//给点u编号
fp[sz] = u;
bfs(u);
}
}
build(, n, );
char op[];
int k, d;
scanf("%d", &q);
while (q--) {
scanf("%s", op);
if (op[] == 'M') {
scanf("%d%d%d", &u, &k, &d);
change(u, k, d);
} else {
scanf("%d%d", &u, &k);
printf("%d\n", get_ans(u, k));
}
}
}
}

HDU5957 Query on a graph(拓扑找环,BFS序,线段树更新,分类讨论)的更多相关文章

  1. 牛客练习赛11 假的字符串 (Trie树+拓扑找环)

    牛客练习赛11 假的字符串 (Trie树+拓扑找环) 链接:https://ac.nowcoder.com/acm/problem/15049 来源:牛客网 给定n个字符串,互不相等,你可以任意指定字 ...

  2. 【BZOJ-3832】Rally 拓扑序 + 线段树 (神思路题!)

    3832: [Poi2014]Rally Time Limit: 20 Sec  Memory Limit: 128 MBSec  Special JudgeSubmit: 168  Solved:  ...

  3. HDU 5957 Query on a graph (拓扑 + bfs序 + 树剖 + 线段树)

    题意:一个图有n个点,n条边,定义D(u,v)为u到v的距离,S(u,k)为所有D(u,v)<=k的节点v的集合 有m次询问(0<=k<=2): 1 u k d:将集合S(u,k)的 ...

  4. POJ 2553 The Bottom of a Graph Tarjan找环缩点(题解解释输入)

    Description We will use the following (standard) definitions from graph theory. Let V be a nonempty ...

  5. S - Query on a tree HDU - 3804 线段树+dfs序

    S - Query on a tree HDU - 3804   离散化+权值线段树 题目大意:给你一棵树,让你求这棵树上询问的点到根节点直接最大小于等于val的长度. 这个题目和之前写的那个给你一棵 ...

  6. 【CodeForces】915 D. Almost Acyclic Graph 拓扑排序找环

    [题目]D. Almost Acyclic Graph [题意]给定n个点的有向图(无重边),问能否删除一条边使得全图无环.n<=500,m<=10^5. [算法]拓扑排序 [题解]找到一 ...

  7. HDU 5957 Query on a graph

    HDU 5957 Query on a graph 2016ACM/ICPC亚洲区沈阳站 题意 \(N(N \le 10^5)\)个点,\(N\)条边的连通图. 有\(M \le 10^5\)操作: ...

  8. E. Andrew and Taxi(二分+拓扑判环)

    题目链接:http://codeforces.com/contest/1100/problem/E 题目大意:给你n和m,n代表有n个城市,m代表有m条边,然后m行输入三个数,起点,终点,花费.,每一 ...

  9. Codeforces Beta Round #88 C. Cycle —— DFS(找环)

    题目链接:http://codeforces.com/problemset/problem/117/C C. Cycle time limit per test 2.5 seconds memory ...

随机推荐

  1. [SHELL]退出脚本

    一,退出状态码 1,范围:0~255 2,查看退出状态码:必须在命令执行之后立即执行 ,显示的是脚本最后一条命令的退出状态码 echo $? 若f返回值为0,则表示正常 有异常为正值 二,exit 脚 ...

  2. redis 面试

    Redis有哪些数据结构? 字符串String.字典Hash.列表List.集合Set.有序集合SortedSet. 如果你是Redis中高级用户,还需要加上下面几种数据结构HyperLogLog.G ...

  3. 转战Java~

    记得16年5月份开始学的Java,当时就是为了学Hadoop才学的Java基础,之后Hadoop没学成,倒是学了Java Web的东西,当时就是玩玩,然后弄了个WeChat后台,就完事了.然后就又回到 ...

  4. 六: Image Viewer 离线镜像查看器

    参考:http://hadoop.apache.org/docs/r2.6.3/hadoop-project-dist/hadoop-hdfs/HdfsImageViewer.html   离线镜像查 ...

  5. Pipeline组Alpha版本发布说明

    Pipeline组Alpha版本发布说明 项目名称 Pipeline 项目版本 Alpha版本 负责人 北京航空航天大学计算机学院 ILoveSE 联系方式 http://www.cnblogs.co ...

  6. 20145214 《Java程序设计》第8周学习总结

    20145214 <Java程序设计>第8周学习总结 教材学习内容总结 日志API 使用日志的起点是Logger类,Logger类的构造函数标示为protected,不是java.util ...

  7. 对Objective-C中runtime的理解

    Objective-C是面向runtime(运行时)的语言,在应用程序运行的时候来决定函数内部实现什么以及做出其它决定的语言.程序员可以在程序运行时创建,检 查,修改类,对象和它们的方法,Object ...

  8. # 团队作业MD

    队员姓名与学号 051501124 王彬(组长) 111500206 赵畅 031602215 胡展瑞 031602320 李恒达 031602131 佘岳昕 031602431 王源 0316022 ...

  9. TCP系列10—连接管理—9、syncookie、fastopen与backlog

    这部分内容涉及较多linux实现,可以跳过. 一.listen系统调用对backlog的处理 当socket处于LISTEN或者CLOSED状态时,fastopen队列的长度可以通过TCP_FASTO ...

  10. Unity3d学习日记(二)

      跟着教程做让背景可以滚动起来并添加了背景的粒子特效,加入了敌机.   ctrl攻击,↑↓←→移动,Game Over后按R重新开始游戏.   Space Shooter游戏地址:http://ya ...