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)就把此边加入栈(可能要记一下 ...
随机推荐
- mapkeeper总结
1. mapkeeper下有个thrift目录,在该目录编译c++版本和java版本. C++版本需要系统安装thrift-0.9.2,而java版本需要系统安装thrift-0.8.0 2. thr ...
- log4j 配置文件详解
[1]从零开始 a). 新建Java Project>>新建package>>新建java类: b). import jar包(一个就够),这里我用的是log4j-1.2.14 ...
- Linux-打包和文件系统
1 文件后缀 在Linux中后缀没有实际意义 2 打包文件 tar c 创建一个归档 v 查看过程 f 指定文件名 t 列出归档内容 x 从归档中解压出文件 C 改变到哪个目录 z gzip压缩 j ...
- java虚拟机内存溢出和泄漏实例
测试参数设置: 1.循环调用new A()实现堆溢出,java.lang.OutOfMemoryError: Java heap space, 虚拟机参数:-Xms1M -Xmx1M -XX:+Hea ...
- php之上传图片及传数据到mysql
index.html <form action="php.php" method="post" enctype="multipart/form- ...
- Watir RAutomation VS AutoIt to deal with popup
Watir使用过程中,经常遇到的弹出框分为几类: 1. Alert 2. Confirm 3. Prompt 4. File select Watir 处理弹出框,有很多方法,可以应用 AutoIt来 ...
- kafka数据可靠性深度解读【转】
1 概述 Kakfa起初是由LinkedIn公司开发的一个分布式的消息系统,后成为Apache的一部分,它使用Scala编写,以可水平扩展和高吞吐率而被广泛使用.目前越来越多的开源分布式处理系统如Cl ...
- MSSQL 调用 .net 代码
http://www.cnblogs.com/laozhao8/p/3398681.html 在SQL Server中调用.NET程序集 需求是这样的,我在.net程序里操作数据时将一些字段数据加 ...
- %02d %03d
strTemp.Format("%02d",m_unEditPosition); %02d 输出两位整数,不足两位的前面加0,比如05,06…… %03d 输出三位整数,不足两位的 ...
- Linux Ctrl+Z的使用方法
假设你发现前台运行的一个程序需要很长的时间,但是需要干其他的事情,你就可以用 Ctrl-Z ,终止这个程序,然后可以看到系统提示: [1]+ Stopped /root/bin/rsync.sh 然后 ...