HDU5957 Query on a graph(拓扑找环,BFS序,线段树更新,分类讨论)
传送门: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序,线段树更新,分类讨论)的更多相关文章
- 牛客练习赛11 假的字符串 (Trie树+拓扑找环)
牛客练习赛11 假的字符串 (Trie树+拓扑找环) 链接:https://ac.nowcoder.com/acm/problem/15049 来源:牛客网 给定n个字符串,互不相等,你可以任意指定字 ...
- 【BZOJ-3832】Rally 拓扑序 + 线段树 (神思路题!)
3832: [Poi2014]Rally Time Limit: 20 Sec Memory Limit: 128 MBSec Special JudgeSubmit: 168 Solved: ...
- 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)的 ...
- POJ 2553 The Bottom of a Graph Tarjan找环缩点(题解解释输入)
Description We will use the following (standard) definitions from graph theory. Let V be a nonempty ...
- S - Query on a tree HDU - 3804 线段树+dfs序
S - Query on a tree HDU - 3804 离散化+权值线段树 题目大意:给你一棵树,让你求这棵树上询问的点到根节点直接最大小于等于val的长度. 这个题目和之前写的那个给你一棵 ...
- 【CodeForces】915 D. Almost Acyclic Graph 拓扑排序找环
[题目]D. Almost Acyclic Graph [题意]给定n个点的有向图(无重边),问能否删除一条边使得全图无环.n<=500,m<=10^5. [算法]拓扑排序 [题解]找到一 ...
- HDU 5957 Query on a graph
HDU 5957 Query on a graph 2016ACM/ICPC亚洲区沈阳站 题意 \(N(N \le 10^5)\)个点,\(N\)条边的连通图. 有\(M \le 10^5\)操作: ...
- E. Andrew and Taxi(二分+拓扑判环)
题目链接:http://codeforces.com/contest/1100/problem/E 题目大意:给你n和m,n代表有n个城市,m代表有m条边,然后m行输入三个数,起点,终点,花费.,每一 ...
- Codeforces Beta Round #88 C. Cycle —— DFS(找环)
题目链接:http://codeforces.com/problemset/problem/117/C C. Cycle time limit per test 2.5 seconds memory ...
随机推荐
- sqlalchemy 转json 的几种常用方式
sqlalchemy 转json 的几种常用方式 # -*- coding:utf-8 -*- import datetime from flask import Flask, json, jsoni ...
- 解决ssh_exchange_identification:read connection reset by peer 原因
服务器改了密码,试过密码多次后出现: ssh_exchange_identification: read: Connection reset by peer 可以通过ssh -v查看连接时详情 Ope ...
- 总结获取原生JS(javascript)基本操作
var a = document.getElementByIdx_x_x("dom"); jsCopy(a);//调用清理空格的函数 var b = a.childNodes;// ...
- 应用Response.Write实现带有进度条的多文件上传
前几天,写过一篇随笔“使用RESPONSE.WRITE实现在页面的生命周期中前后台的交互”.说是交互,实际上也主要是在ASP.NET的页面周期中 从后台利用RESPONSE.WRITE向前台即时的推送 ...
- Python高级编程-itertoos模块
Python的内建模块itertools提供了非常有用的用于操作迭代对象的函数. 首先我们看看itertools模块提供的几个“无限”迭代器, import itertools naturals = ...
- 20145214 《Java程序设计》第6周学习总结
20145214 <Java程序设计>第6周学习总结 教材学习内容总结 串流设计 Java将输入/输出抽象化为串流,数据有来源及目的地,衔接两者的是串流对象. 输入串流代表对象为java. ...
- LintCode-165.合并两个排序链表
合并两个排序链表 将两个排序链表合并为一个新的排序链表 样例 给出 1->3->8->11->15->null,2->null, 返回 1->2->3- ...
- udp->ip & tcp->ip 发送数据包的目的地址的源地址是什么时候确定的?
udp->ip & tcp->ip udp到ip层是:ip_send_skb tcp到ip层是: ip_queue_xmit 拿tcp为例,在使用[ip_queue_xmit, i ...
- AndroidStudio3.0 注解报错Annotation processors must be explicitly declared now. The following dependencies on the compile classpath are found to contain annotation processor.
把Androidstudio2.2的项目放到3.0里面去了,然后开始报错了. 体验最新版AndroidStudio3.0 Canary 8的时候,发现之前项目的butter knife报错,用到注解的 ...
- InstallShield Limited Edition for Visual Studio 国内注册时国家无下拉框解决方法
注册地址:http://learn.flexerasoftware.com/content/IS-EVAL-InstallShield-Limited-Edition-Visual-Studio 火狐 ...