洛谷P2783 有机化学之神偶尔会做作弊(水黑题系列)
12 March 2019 |
duanyll | Tags:
OI
题解
LCA
SCC
这个题看上去好像是tarjan缩点后直接LCA判距离,其实也是这样……
但是一般的tarjan求SCC写法过不了,题目也强调了两个碳不成环,因此可以
- 先DFS一遍双向边变单向边
- 或者tarjan里面加一个
v!=fa
就好
我写的是后一种
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#include <iomanip>
using namespace std;
const int MAXN = 10010;
const int MAXM = 50010;
const int INF = 0x3f3f3f3f;
class LFS {
public:
LFS() {
memset(head, -1, sizeof head);
ecnt = 0;
n = 0;
}
LFS(int N) {
memset(head, -1, sizeof head);
ecnt = 0;
n = N;
}
void adde(int from, int to, int w) {
e[ecnt].to = to;
e[ecnt].w = w;
e[ecnt].next = head[from];
head[from] = ecnt++;
}
void addde(int a, int b, int w) {
adde(a, b, w);
adde(b, a, w);
}
protected:
struct Edge {
int to, next, w;
} e[MAXM * 2];
int head[MAXN];
int ecnt;
int n;
private:
virtual void dfs(int u, int fa) {
for (int i = head[u]; i != -1; i = e[i].next) {
int v = e[i].to;
if (v != fa) {
dfs(v, u);
}
}
}
};
#include <stack>
class SCC_Tarjan : public LFS {
public:
int scccnt;
int belong[MAXN];
SCC_Tarjan(int n) : LFS(n) {
memset(dfn, -1, sizeof dfn);
memset(low, -1, sizeof low);
memset(ins, false, sizeof ins);
memset(belong,0,sizeof belong);
tim = 1;
scccnt = 0;
}
void solve() {
for(int i = 1;i<=n;i++){
if(dfn[i] == -1){
tarjan(i,0);
}
}
}
//缩点,先调用solve
void createnew(LFS* map){
for(int i = 1;i<=n;i++){
for(int j = head[i];j!=-1;j = e[j].next){
int u = belong[i];
int v = belong[e[j].to];
if(u != v){
map->adde(u,v,e[j].w);
}
}
}
}
protected:
stack<int> s;
bool ins[MAXN];
int mina[MAXN];
int low[MAXN], dfn[MAXN];
int tim;
void tarjan(int u,int fa) {
dfn[u] = low[u] = tim++;
s.push(u);
ins[u] = true;
for (int i = head[u]; i != -1; i = e[i].next) {
int v = e[i].to;
if (fa == v){
continue;
}
if (dfn[v] == -1) {
tarjan(v,u);
low[u] = min(low[u], low[v]);
} else {
if (ins[v]) {
low[u] = min(low[u], dfn[v]);
}
}
}
if (dfn[u] == low[u]) {
scccnt++;
int v = 0;
while (v != u) {
v = s.top();
s.pop();
ins[v] = false;
belong[v] = scccnt;
}
}
}
};
class LCA : public LFS{
public:
int dep[MAXN];
LCA(int n) : LFS(n) {
memset(dep, -1, sizeof dep);
}
void pre(int rt = 1) { dfs(rt, 1, 0); }
int querylca(int a, int b) {
if (dep[a] > dep[b]) swap(a, b);
int h = dep[b] - dep[a];
for (int i = 20; i >= 0; i--) {
if(h & (1 << i)) {
b = f[b][i];
}
}
if (a == b) return a;
for (int i = 20; i >= 0; i--) {
if (f[a][i] == f[b][i]) continue;
a = f[a][i];
b = f[b][i];
}
return f[a][0];
}
protected:
int f[MAXN][22];
private:
void dfs(int u, int d, int fa) {
dep[u] = d;
f[u][0] = fa;
for (int i = 1; i < 21; i++) {
f[u][i] = f[f[u][i - 1]][i - 1];
}
for (int i = head[u]; i != -1; i = e[i].next) {
int v = e[i].to;
if (dep[v] == -1) {
dfs(v, d + 1, u);
}
}
}
};
#include <cctype>
#include <cstdio>
inline int read() {
int X = 0, w = 0;
char ch = 0;
while (!isdigit(ch)) {
w |= ch == '-';
ch = getchar();
}
while (isdigit(ch)) {
X = (X << 3) + (X << 1) + (ch ^ 48);
ch = getchar();
}
return w ? -X : X;
}
int main(){
int n = read();
int m = read();
SCC_Tarjan *all = new SCC_Tarjan(n);
for(int i = 1;i<=m;i++){
int u = read();
int v = read();
all->addde(u,v,1);
}
all->solve();
LCA* map = new LCA(all->scccnt);
all->createnew(map);
map->pre();
int tcnt = read();
for(int i = 1;i<=tcnt;i++){
int a,b;
a = all->belong[read()];
b = all->belong[read()];
int lca = map->querylca(a,b);
int ans = map->dep[a] + map->dep[b] - map->dep[lca]*2 + 1;
char tmp[64] = {0};
int pos = 0;
while (ans){
tmp[pos++] = (((bool)(ans & 1)) + '0');
ans >>= 1;
}
for(;pos>0;pos--){
putchar(tmp[pos-1]);
}
putchar('\n');
}
}
别忘了输出二进制