传送门: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. OIDC in Angular 6

    参照 草根专栏- ASP.NET Core + Ng6 实战:https://v.qq.com/x/page/i07702h18nz.html 1. OIDC-Client https://githu ...

  2. JVM--内存模型与线程

    一.硬件与效率的一致性 计算机的存储设备与处理器的运算速度存在几个数量级的差距,现在计算机系统不得不在内存和处理器之间增加一层高速缓存(cache)来作为缓冲.将运算需要的数据复制到缓存中,让运算能够 ...

  3. 将Render博客搬至GIT(偷懒)

    SmallEngine 一个特别小的研究引擎[用于各种实验] 框架上设计上采用Unreal.Unity的设计思路[偷懒了] https://github.com/daozhangXDZ/DZSmall ...

  4. HADOOP docker(十):hdfs 结构体系

    1.简介2.namenode和datanode3.The File System Namespace 文件系统命名空间4.Data Replication 数据复制5.Replica Placemen ...

  5. POJ 1755 Triathlon(线性规划の半平面交)

    Description Triathlon is an athletic contest consisting of three consecutive sections that should be ...

  6. Python—元组(戴上了枷锁的列表)

    一.概念 元组属于不可变序列,元组是包含多个元素的类型,元素之间用逗号分割.例如:t1=123,456,“hello”元组可以是空的,t2=()元组包含一个元素时:t3=123,元组外侧可以使用括号, ...

  7. 11.22Daily Scrum

    人员 任务分配完成情况 明天任务分配 王皓南 实现网页上视频浏览的功能.研究相关的代码和功能.979 数据库测试 申开亮 实现网页上视频浏览的功能.研究相关的代码和功能.978 实现视频浏览的功能 王 ...

  8. web前端之路 - 开篇

    一 web发展历程 了解事物的历史有助于我们渐进式的从发展的思路清楚了解事物的来龙去脉. 这里有一篇网文写得比较清晰和完整:https://www.tianmaying.com/tutorial/we ...

  9. 移动端的picker参考vux

    参考vux移动端的ui组件,做了一个picker,测试在微信,uc主流浏览器能够正常工作.而在华为浏览器根本不能使用.而测试了vux的原有picker组件,发现在华为自带浏览器中,效果依然能够实现. ...

  10. cmake & make

    大家都知道,写程序大体步骤为: 1.用编辑器编写源代码,如.c文件. 2.用编译器编译代码生成目标文件,如.o. 3.用链接器连接目标代码生成可执行文件,如.exe. 但如果源文件太多,一个一个编译时 ...