hdu 3686 Traffic Real Time Query System 点双两通分量 + LCA。这题有重边!!!
http://acm.hdu.edu.cn/showproblem.php?pid=3686
我要把这题记录下来。
一直wa。
自己生成数据都是AC的。现在还是wa。留坑。
我感觉我现在倒下去床上就能睡着了。
不知道是我的LCA错了,还是tarjan
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <assert.h>
#define IOS ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL; #include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <bitset>
const int maxm = + ;
const int maxn = + ;
struct Edge {
int u, v, id, tonext;
} e[maxm * ];
int first[maxm], num;
void addEdge(int u, int v, int id) {
++num;
e[num].u = u, e[num].v = v, e[num].tonext = first[u];
first[u] = num;
e[num].id = id;
}
int uuu[maxm], vvv[maxm];
int n, m;
int low[maxm], DFN[maxm], st[maxm], id[maxm];
int top, when, toSelid;
bool iscut[maxm];
vector<int>bolg[maxm];
int root;
void tarjan(int cur, int fa) {
DFN[cur] = low[cur] = ++when;
int child = ;
for (int i = first[cur]; i; i = e[i].tonext) {
int v = e[i].v;
if (v == fa) continue;
if (!DFN[v]) {
child++;
st[++top] = e[i].id;
tarjan(v, cur);
low[cur] = min(low[cur], low[v]);
if (low[v] >= DFN[cur]) {
iscut[cur] = true;
// if (cur == root && child < 2) iscut[cur] = false;
++toSelid;
do {
int eID = st[top--];
bolg[uuu[eID]].push_back(toSelid);
bolg[vvv[eID]].push_back(toSelid);
id[eID] = toSelid;
} while (st[top + ] != e[i].id);
} } else if (DFN[cur] > DFN[v]) {
low[cur] = min(low[cur], DFN[v]);
st[++top] = e[i].id;
}
}
}
void solveTarjan(int n) {
memset(DFN, , sizeof DFN);
memset(low, , sizeof low);
memset(iscut, , sizeof iscut);
memset(id, , sizeof id);
for (int i = ; i <= maxm - ; ++i) {
bolg[i].clear();
}
when = top = toSelid = ;
for (int i = ; i <= n; ++i) {
if (!DFN[i]) {
root = i;
tarjan(i, i);
}
}
}
int dis[maxm];
bool treeCut[maxm], vis[maxm];
struct Node {
int cur, cnt;
Node(int _cur, int _cnt) {
cur = _cur, cnt = _cnt;
}
};
void bfs(int be) {
vis[be] = true;
dis[be] = treeCut[be];
queue<struct Node>que;
que.push(Node(be, treeCut[be]));
while (!que.empty()) {
struct Node t = que.front();
que.pop();
for (int i = first[t.cur]; i; i = e[i].tonext) {
int v = e[i].v;
if (vis[v]) continue;
vis[v] = true;
dis[v] = t.cnt + treeCut[v];
que.push(Node(v, t.cnt + treeCut[v]));
}
}
}
int ansc[maxn * ][], deep[maxm], fa[maxm];
void init_LCA(int cur) {
ansc[cur][] = fa[cur]; //跳1步,那么祖先就是爸爸
for (int i = ; i <= ; ++i) { //倍增思路,递归处理
ansc[cur][i] = ansc[ansc[cur][i - ]][i - ];
}
for (int i = first[cur]; i; i = e[i].tonext) {
int v = e[i].v;
if (v == fa[cur]) continue;
fa[v] = cur;
deep[v] = deep[cur] + ;
init_LCA(v);
}
}
int LCA(int x, int y) {
if (deep[x] < deep[y]) swap(x, y); //需要x是最深的
for (int i = ; i >= ; --i) { //从大到小枚举,因为小的更灵活
if (deep[ansc[x][i]] >= deep[y]) { //深度相同,走进去就对了。就是要去到相等。
x = ansc[x][i];
}
}
if (x == y) return x;
for (int i = ; i >= ; --i) {
if (ansc[x][i] != ansc[y][i]) { //走到第一个不等的地方,
x = ansc[x][i];
y = ansc[y][i];
}
}
return ansc[x][]; //再跳一步就是答案
}
void init() {
num = ;
memset(ansc, , sizeof ansc);
memset(deep, , sizeof deep);
memset(fa, , sizeof fa);
memset(dis, , sizeof dis);
memset(treeCut, , sizeof treeCut);
memset(vis, , sizeof vis); }
void work() {
init();
num = ;
memset(first, , sizeof first);
for (int i = ; i <= m; ++i) {
int u, v;
scanf("%d%d", &u, &v);
addEdge(u, v, i);
addEdge(v, u, i);
uuu[i] = u;
vvv[i] = v;
}
solveTarjan(n);
memset(first, , sizeof first);
memset(treeCut, , sizeof treeCut);
num = ;
int to = toSelid;
for (int i = ; i <= n; ++i) {
if (!iscut[i]) continue;
++to;
treeCut[to] = true;
sort(bolg[i].begin(), bolg[i].end());
addEdge(to, bolg[i][], );
addEdge(bolg[i][], to, );
for (int j = ; j < bolg[i].size(); ++j) {
if (bolg[i][j - ] == bolg[i][j]) continue;
addEdge(to, bolg[i][j], );
addEdge(bolg[i][j], to, );
}
}
// int tot = 2;
// for (int i = 0; i < bolg[tot].size(); ++i) {
// cout << bolg[tot][i] << " ";
// }
// cout << endl;
memset(vis, false, sizeof vis);
memset(fa, , sizeof fa);
memset(deep, , sizeof deep);
memset(ansc, , sizeof ansc);
memset(dis, , sizeof dis);
for (int i = ; i <= to; ++i) {
if (vis[i]) continue;
bfs(i);
fa[i] = i;
deep[i] = ;
init_LCA(i);
}
// cout << iscut[11] << " ***" << endl;
int q;
scanf("%d", &q);
while (q--) {
int x, y;
scanf("%d%d", &x, &y);
x = id[x];
y = id[y];
if (x == y) {
printf("0\n");
continue;
}
int res = LCA(x, y);
if (res == x) {
assert(treeCut[x] == );
}
if (res == y) {
assert(treeCut[y] == );
}
int ans = dis[x] + dis[y] - * dis[res];
assert(ans + treeCut[res] >= );
printf("%d\n", ans + treeCut[res]);
}
} int main() {
#ifdef local
freopen("data.txt", "r", stdin);
// freopen("data.txt", "w", stdout);
#endif
while (scanf("%d%d", &n, &m) != EOF && (n + m)) work();
return ;
}
原来这题是有重边的,怪不得我一直wa。而且自己生成的数据都是没有重边的,TAT
说下思路,
题目要求的经过多少个割点,
首先,题目给出的是从边s走到边t。这样很好办,一开始以为是点s做到点t,这样比较麻烦。
先做一次点双连通分量,对边分好id。然后对于每一个割点,把和它连接的边重建一颗树。然后这个割点就是这棵树的treeCut
这就相当于给出一颗树,树上有些点(那副图的边变成点了)是染色了的,问从点s走到点t,最少走过多少个染色的点。
这样可以预处理出dis[i]表示从树根走到点i,一共经过多少个染色的点,这样就相当于区间减法,然后配合lca即可。
1、题目虽然保证roads到rodat有路,但是可能有多个连通分支。
2、有重边。
其实有重边不用怕,没什么的,只需要标记那条边是用过了的就可以。
既可以判断不返回fa,又可以判重边。
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <assert.h>
#define IOS ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL; #include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <bitset>
const int maxm = + ;
const int maxn = + ;
struct Edge {
int u, v, id, tonext;
} e[maxm * ];
int first[maxm], num;
void addEdge(int u, int v, int id) {
++num;
e[num].u = u, e[num].v = v, e[num].tonext = first[u];
first[u] = num;
e[num].id = id;
}
int uuu[maxm], vvv[maxm];
int n, m;
int low[maxm], DFN[maxm], st[maxm], id[maxm];
int top, when, toSelid;
bool iscut[maxm];
vector<int>bolg[maxm];
int root;
void tarjan(int cur, int fa, int fromID) {
DFN[cur] = low[cur] = ++when;
int child = ;
for (int i = first[cur]; i; i = e[i].tonext) {
int v = e[i].v;
if (v == fa && e[i].id == fromID) continue;
if (!DFN[v]) {
child++;
st[++top] = e[i].id;
tarjan(v, cur, e[i].id);
low[cur] = min(low[cur], low[v]);
if (low[v] >= DFN[cur]) {
iscut[cur] = true;
// if (cur == root && child < 2) iscut[cur] = false;
++toSelid;
do {
int eID = st[top--];
bolg[uuu[eID]].push_back(toSelid);
bolg[vvv[eID]].push_back(toSelid);
id[eID] = toSelid;
} while (st[top + ] != e[i].id);
} } else if (DFN[cur] > DFN[v]) {
low[cur] = min(low[cur], DFN[v]);
st[++top] = e[i].id;
}
}
}
void solveTarjan(int n) {
memset(DFN, , sizeof DFN);
memset(low, , sizeof low);
memset(iscut, , sizeof iscut);
memset(id, , sizeof id);
for (int i = ; i <= maxm - ; ++i) {
bolg[i].clear();
}
when = top = toSelid = ;
for (int i = ; i <= n; ++i) {
if (!DFN[i]) {
root = i;
tarjan(i, i, -);
}
}
}
int dis[maxm];
bool treeCut[maxm], vis[maxm];
struct Node {
int cur, cnt;
Node(int _cur, int _cnt) {
cur = _cur, cnt = _cnt;
}
};
void bfs(int be) {
vis[be] = true;
dis[be] = treeCut[be];
queue<struct Node>que;
que.push(Node(be, treeCut[be]));
while (!que.empty()) {
struct Node t = que.front();
que.pop();
for (int i = first[t.cur]; i; i = e[i].tonext) {
int v = e[i].v;
if (vis[v]) continue;
vis[v] = true;
dis[v] = t.cnt + treeCut[v];
que.push(Node(v, t.cnt + treeCut[v]));
}
}
}
int ansc[maxn * ][], deep[maxm], fa[maxm];
void init_LCA(int cur) {
ansc[cur][] = fa[cur]; //跳1步,那么祖先就是爸爸
for (int i = ; i <= ; ++i) { //倍增思路,递归处理
ansc[cur][i] = ansc[ansc[cur][i - ]][i - ];
}
for (int i = first[cur]; i; i = e[i].tonext) {
int v = e[i].v;
if (v == fa[cur]) continue;
fa[v] = cur;
deep[v] = deep[cur] + ;
init_LCA(v);
}
}
int LCA(int x, int y) {
if (deep[x] < deep[y]) swap(x, y); //需要x是最深的
for (int i = ; i >= ; --i) { //从大到小枚举,因为小的更灵活
if (deep[ansc[x][i]] >= deep[y]) { //深度相同,走进去就对了。就是要去到相等。
x = ansc[x][i];
}
}
if (x == y) return x;
for (int i = ; i >= ; --i) {
if (ansc[x][i] != ansc[y][i]) { //走到第一个不等的地方,
x = ansc[x][i];
y = ansc[y][i];
}
}
return ansc[x][]; //再跳一步就是答案
}
void work() {
num = ;
memset(first, , sizeof first);
for (int i = ; i <= m; ++i) {
int u, v;
scanf("%d%d", &u, &v);
addEdge(u, v, i);
addEdge(v, u, i);
uuu[i] = u;
vvv[i] = v;
}
solveTarjan(n);
memset(first, , sizeof first);
memset(treeCut, , sizeof treeCut);
num = ;
int to = toSelid;
for (int i = ; i <= n; ++i) {
if (!iscut[i]) continue;
++to;
treeCut[to] = true;
sort(bolg[i].begin(), bolg[i].end());
addEdge(to, bolg[i][], );
addEdge(bolg[i][], to, );
for (int j = ; j < bolg[i].size(); ++j) {
if (bolg[i][j - ] == bolg[i][j]) continue;
addEdge(to, bolg[i][j], );
addEdge(bolg[i][j], to, );
}
}
// int tot = 2;
// for (int i = 0; i < bolg[tot].size(); ++i) {
// cout << bolg[tot][i] << " ";
// }
// cout << endl;
memset(vis, false, sizeof vis);
for (int i = ; i <= to; ++i) {
if (vis[i]) continue;
bfs(i);
fa[i] = i;
deep[i] = ;
init_LCA(i);
}
// cout << iscut[11] << " ***" << endl;
int q;
scanf("%d", &q);
while (q--) {
int x, y;
scanf("%d%d", &x, &y);
x = id[x];
y = id[y];
if (x == y) {
printf("0\n");
continue;
}
int res = LCA(x, y);
if (res == x) {
assert(treeCut[x] == );
}
if (res == y) {
assert(treeCut[y] == );
}
int ans = dis[x] + dis[y] - * dis[res];
assert(ans + treeCut[res] >= );
printf("%d\n", ans + treeCut[res]);
}
} int main() {
#ifdef local
freopen("data.txt", "r", stdin);
// freopen("data.txt", "w", stdout);
#endif
while (scanf("%d%d", &n, &m) != EOF && (n + m)) work();
return ;
}
代码写的很烂,因为wa了3小时,一直在yy改错。
hdu 3686 Traffic Real Time Query System 点双两通分量 + LCA。这题有重边!!!的更多相关文章
- HDU 3686 Traffic Real Time Query System(双连通分量缩点+LCA)(2010 Asia Hangzhou Regional Contest)
Problem Description City C is really a nightmare of all drivers for its traffic jams. To solve the t ...
- HDU 3686 Traffic Real Time Query System (图论)
HDU 3686 Traffic Real Time Query System 题目大意 给一个N个点M条边的无向图,然后有Q个询问X,Y,问第X边到第Y边必需要经过的点有多少个. solution ...
- HDU 3686 Traffic Real Time Query System(点双连通)
题意 给定一张 \(n\) 个点 \(m\) 条边的无向图,\(q\) 次询问,每次询问两边之间的必经之点个数. 思路 求两点之间必经之边的个数用的是边双缩点,再求树上距离.而对比边双和点双之 ...
- 【HDOJ】3686 Traffic Real Time Query System
这题做了几个小时,基本思路肯定是求两点路径中的割点数目,思路是tarjan缩点,然后以割点和连通块作为新节点见图.转化为lca求解.结合点——双连通分量与LCA. /* 3686 */ #includ ...
- 【Targan+LCA】HDU 3686 Traffic Real Time Query
题目内容 洛谷链接 给出一个\(n\)个节点,\(m\)条边的无向图和两个节点\(s\)和\(t\),问这两个节点的路径中有几个点必须经过. 输入格式 第一行是\(n\)和\(m\). 接下来\(m\ ...
- CH#24C 逃不掉的路 和 HDU3686 Traffic Real Time Query System
逃不掉的路 CH Round #24 - 三体杯 Round #1 题目描述 现代社会,路是必不可少的.任意两个城镇都有路相连,而且往往不止一条.但有些路连年被各种XXOO,走着很不爽.按理说条条大路 ...
- HDU3686 Traffic Real Time Query System 题解
题目 City C is really a nightmare of all drivers for its traffic jams. To solve the traffic problem, t ...
- Traffic Real Time Query System 圆方树+LCA
题目描述 City C is really a nightmare of all drivers for its traffic jams. To solve the traffic problem, ...
- Traffic Real Time Query System HDU - 3686
https://vjudge.net/problem/HDU-3686 点双啊,就是在求割顶的时候,另外用一个栈来存一些边 在遍历u点出发的边时,遇到树边或反向边(u,v)就把此边加入栈(可能要记一下 ...
随机推荐
- SpringMVC与Struts2区别与比较
1.Struts2是类级别的拦截, 一个类对应一个request上下文,SpringMVC是方法级别的拦截,一个方法对应一个request上下文,而方法同时又跟一个url对应,所以说从架构本身上Spr ...
- 查看系统信息,区分Centos和Ubuntu
查看系统信息,区分Centos和Ubuntu # cat /etc/issue \S Kernel \r on an \m centos $ cat /etc/issue Ubuntu 16.04.4 ...
- codeforces B. Polo the Penguin and Matrix 解题报告
题目链接:http://codeforces.com/problemset/problem/289/B 题目意思:给出一个 n 行 m 列的矩阵和数值 d .通过对矩阵里面的数进行 + d 或者 - ...
- powershell 扩展 (PSCX) 安装指南
在玩ansible的过程中,使用win_unzip模块时powershell支持不了,需要安装PSCX对powershell进行扩展,随手记录下安装过程. 从官网下载的Pscx是一个zip压缩文件,解 ...
- easyui-tabs 在ie8下基于iframe嵌套页面加载成功后切换空白问题
这是一个很坑的问题,由于项目必须支持ie8的情况下,产生了这个问题.在我进行逐步对比的分析过后,终于发现了原因所在:
- 51Nod - 1295:XOR key (可持久化Trie求区间最大异或)
给出一个长度为N的正整数数组A,再给出Q个查询,每个查询包括3个数,L, R, X (L <= R).求ALL 至 ARR 这R - L + 1个数中,与X 进行异或运算(Xor),得到的最大值 ...
- POJ 2970 The lazy programmer(贪心+单调优先队列)
A new web-design studio, called SMART (Simply Masters of ART), employs two people. The first one is ...
- 无向图hash
一个效果还行的 无向图hash判同构的方法 求出每个点向其它点的最短路,然后排序,然后按字符串拼接起来,再按每个点的字符串 排序后的rank 作为每一个点的初始hash值 然后每一轮,把每个点的相邻点 ...
- linux 查看某进程 并杀死进程 ps grep kill
Linux 中使用top 或 ps 查看进程使用kill杀死进程 1.使用top查看进程: $top 进行执行如上命令即可查看top!但是难点在如何以进程的cpu占用量进行排序呢? cpu占用量排序执 ...
- Nwjs简单配置
1.创建一个工程,配置一个 package.json 文件 { "name": "application-name", "version" ...