传送门: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. Python全栈 Web(HTML基础语法)

    原文地址:           https://yq.aliyun.com/articles/632672 .............................................. ...

  2. vuex-Actions的用法

    Action 类似于 mutation,不同在于: Action 提交的是 mutation,而不是直接变更状态. Action 是异步的,mutation是同步的. 沿用vuex学习---简介的案例 ...

  3. 京东2018秋招c++岗 神奇数

    题意大概是: 一个数比如242,把所有数字分成两组,而且两组的和相等,那么这个数就是神奇数,此时242,能够分成{2,2}和{4},所以242是神奇数. 题目要求输入n和m求[n,m]区间内神奇数的个 ...

  4. UVALive 3668 A Funny Stone Game(博弈)

    Description The funny stone game is coming. There are n piles of stones, numbered with 0, 1, 2,...,  ...

  5. mysql分页的limit优化

    1.很多新人都会很纳闷,为什么我建了索引使用分页还是这么卡.好,现在让我们一步一步去找原因吧. 首先limit本身跟索引没有直接关系. 先建一张商品sku表 create table goods_sk ...

  6. LintCode-54.转换字符串到整数

    转换字符串到整数 实现atoi这个函数,将一个字符串转换为整数.如果没有合法的整数,返回0.如果整数超出了32位整数的范围,返回INT_MAX(2147483647)如果是正整数,或者INT_MIN( ...

  7. spring ioc经典总结

    component-scan标签默认情况下自动扫描指定路径下的包(含所有子包),将带有 @Component @Repository @Service @Controller标签的类自动注册到spri ...

  8. phpcms v9 thumb(缩略图) 函数说明

    打开phcmsc/libs/functions/global.func.php文件,找到如下代码:/** * 生成缩略图函数 * @param  $imgurl 图片路径 * @param  $wid ...

  9. C#常见函数

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  10. Binding自动侦听

    WPF的强大之一就是数据绑定,Binding是数据桥梁,它的两端是分别是源(Source)和目标(Target),一个简单的类的属性值发生变化,会自动反映在UI界面上,这个属性就是Binding的Pa ...