题目本质:图论,强连通分量,最近公共祖先LCA
这道题之前做过,那会是打暴力打了好像是27分还是37分来着,这次再做题面改了没看出来,放在T4以为很难,实际上难度还可以,连暴力都没打。下面是说说解题思路:
首先我们会由 "所有的环状碳都变成了一个碳" 想到要缩点。但是无向图怎么缩点呢?我们可以按照原来无向图那样缩点,但要注意的一点是 to!=fa[x]。因为这是无向图,可能有的边会直接连向他父亲,假如我们要走这条边的话,就会重复搜,就这样一直无限循环下去。剩下的就和有向图的缩点没什么区别了。接着我们就要考虑每个询问。我们把所有的环去掉后,就会得到一个有向无环图(树)。那么问题就会转化为树上问题。
先给暴力代码吧
27pts
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=100005;
ll read() {
ll x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9') {
if (c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
return x * f;
}
struct Edge {
ll from, to, pre;
} edge[N], e[N];
ll last[N];
ll cnt = 1, nlast[N];
ll ncnt = 1;;
void add(ll x, ll y) {
++cnt;
edge[cnt].from = x;
edge[cnt].to = y;
edge[cnt].pre = last[x];
last[x] = cnt;
}
void nadd(ll x, ll y) {
++ncnt;
e[ncnt].from = x;
e[ncnt].to = y;
e[ncnt].pre = nlast[x];
nlast[x] = ncnt;
}
ll dfn[N], low[N];
bool bridge[N];
ll num = 0, qnum[N];
void tarjan(ll x, ll y) {
dfn[x] = low[x] = ++num;
for (ll i = last[x]; i; i = edge[i].pre) {
ll v = edge[i].to;
if (!dfn[v]) {
tarjan(v, i);
low[x] = min(low[x], low[v]);
if (low[v] > dfn[x]) {
bridge[i] = bridge[i ^ 1] = true;
++qnum[0];
qnum[qnum[0]] = i;
}
} else if ((i ^ 1) != y) low[x] = min(low[x], dfn[v]);
}
return;
}
ll sltnum = 0, belong[N];
void dfs(long long x) {
belong[x] = sltnum;
for (long long i = last[x]; i; i = edge[i].pre) {
if (bridge[i] || belong[edge[i].to]) continue;
dfs(edge[i].to);
}
return;
}
ll f[N][18], deep[N];
void st(ll x, ll fa) {
deep[x] = deep[fa] + 1;
for (int i = nlast[x]; i; i = e[i].pre) {
if (e[i].to == fa) continue;
f[e[i].to][0] = x;
st(e[i].to, x);
}
ll len = log2(deep[x]) + 1;
for (int i = 1; i <= len; ++i){
f[x][i] = f[f[x][i - 1]][i - 1];
}
return;
}
ll LCA(ll x,ll y) {
if (deep[x] > deep[y]){
swap(x, y);
}
int len = log2(deep[y] - deep[x]) + 1;
for (int i = len; i >= 0; --i) {
if (deep[f[y][i]] >= deep[x]) y = f[y][i];
}
if (x == y) return x;
len = log2(deep[x]) + 1;
for (int i = len; i >= 0; --i) {
if (f[x][i] != f[y][i]) {
x = f[x][i];
y = f[y][i];
}
}
return f[x][0];
}
void write(ll x) {
if (x == 0) {
printf("0");
return;
}
if (x == 1) {
printf("1");
return;
}
write(x >> 1);
printf("%lld", x % 2);
}
ll n, m, q;
int main() {
n = read();
m = read();
ll u, v;
for (int i = 1; i <= m; ++i) {
u = read();
v = read();
add(u, v);
add(v, u);
}
tarjan(1, 0);
for (int i = 1; i <= n; ++i) {
if (!belong[i]) {
++sltnum;
dfs(i);
}
}
for (int i = 1; i <= qnum[0]; ++i) {
nadd(belong[edge[qnum[i]].from], belong[edge[qnum[i]].to]);
nadd(belong[edge[qnum[i]].to], belong[edge[qnum[i]].from]);
}
q = read();
st(1, 0);
ll a, b;
for (int i = 1; i <= q; ++i) {
a = read();
b = read();
a = belong[a];
b = belong[b];
write(deep[a] + deep[b] - 2 * deep[LCA(a, b)] + 1);
printf("\n");
}
return 0;
}
18pts
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e4+10;
int read() {
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')x = x * 10 + ch - '0', ch = getchar();
return x * f;
}
int t[10010];
void ot(int x) {
int xx = 0;
for (; x; x >>= 1) {
xx++;
if (x & 1) t[xx] = 1;
else t[xx] = 0;
}
for (int i = xx; i >= 1; i-- ) printf("%d", t[i]);
printf("\n");
}
int head2[N], tot;
struct node {
int to, nxt;
} e[N ], ne[N ];
void add(int a, int b) {
tot++;
e[tot].nxt = head2[a];
e[tot].to = b;
head2[a] = tot;
}
int n, m;
int head22[N], tot2;
void add2(int a, int b) {
tot++;
ne[tot].to = b;
ne[tot].nxt = head22[a];
head22[a] = tot;
}
int f[N][20], dep[N];
int cnt;
void pre() {
for (int j = 1; (1 << j) <= cnt; j++) {
for (int i = 1; i <= cnt; i++) {
if (f[i][j - 1] != -1) {
f[i][j] = f[f[i][j - 1]][j - 1];
}
}
}
}
void dfs(int x, int fa) { //当前节点,当前节点的父节点
dep[x] = dep[fa] + 1;
for (int i = head22[x]; i != -1; i = ne[i].nxt) {
int y = ne[i].to;
if (y != fa) {
f[y][0] = x;
dfs(y, x);
}
}
}
int lca(int x, int y) {
if (dep[x] > dep[y]) swap(x, y);
int k = log2(dep[y]) + 1;
for (int i = k; i >= 0; i--) {
if (dep[y] - (1 << i) >= dep[x]) y = f[y][i];
}
if (x == y) {
return x;
}
for (int i = k; i >= 0; i--) {
if (f[x][i] != -1 && f[x][i] != f[y][i]) {
x = f[x][i];
y = f[y][i];
}
}
//最后X是最近公共祖先的子节点
return f[x][0];
}
//缩点,建新图
int root;
int scc[N];
int dfn[N], low[N], tt;
int vis[N] = {false};
int in[N];
stack<int> stk;
queue<int> q[N];
//bool pan[N]={false};
void ta(int x, int fa) {
dfn[x] = low[x] = ++tt;
//if(!pan[x]){
// cnt++;q[cnt].push(x);
//cout<<x<<"hkjh"<<endl;
// return;
//}
stk.push(x);
in[x] = 1;
int child = 0;
for (int i = head2[x]; i; i = e[i].nxt) {
//sas=true;
int y = e[i].to;
if (y == fa) continue;
//if(x==y)
if (!dfn[y]) {
ta(y, x);
low[x] = min(low[x], low[y]);
//if(low[y]>=dfn[x]){
// child++;
// if(child>1||x!=root){
// vis[x]=1;
// }
/// cnt++;
// while(1){
// int z=stk.top();stk.pop();
// scc[z]=cnt;
// q[cnt].push(z);
// if(z==y) break;
// }
// q[cnt].push(x);
// scc[x]=cnt;
//
} else if (in[y]) low[x] = min(low[x], dfn[y]);
}
if (dfn[x] == low[x]) {
cnt++;
int y;
do {
y = stk.top();
stk.pop(), in[y] = 0;
scc[y] = cnt;
} while (y != x);
}
}
signed main() {
n = read(), m = read();
for (int i = 1; i <= m; i++) {
int a = read(), b = read();
add(a, b);
add(b, a);
}
for (int i = 1; i <= n; i++) {
if (!dfn[i]) {
root = i, ta(i, 0);
}
}
//cout<<cnt<<endl;
memset(head22, -1, sizeof(head22));
memset(f, -1, sizeof f);
for (int x = 1; x <= n; x++) {
for (int i = head2[x]; i; i = e[i].nxt) {
int y = e[i].to;
if (scc[x] != scc[y]) add2(scc[x], scc[y]); //cout<<scc[x]<<" "<<scc[y]<<endl;
}
}
dfs(1, 0);
pre();
int T = read();
for (int i = 1; i <= T; i++) {
int a = read(), b = read();
int k = lca(a, b);
//cout<<k<<endl;
ot(dep[a] + dep[b] + 1 - 2 * dep[k]);
//puts("");
}
return 0;
}
AC
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
int n,m,u,v,x,y,tot,sum,cnt,num,topp,T;
int dep[N],fa[N],size[N],top[N],head[N],hed[N];
int shu[N],dfn[N],low[N],sta[N],son[N];
int t[N];
bool vis[N];
inline int read()//标准快读
{
int s = 0,w = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){s = s * 10+ch -'0'; ch = getchar();}
return s * w;
}
struct node{int to,net;}e[N<<1],edge[N<<1];//为了压行不择手段
void add(int x,int y)
{
e[++tot].to = y;
e[tot].net = head[x];
head[x] = tot;
}
void add_(int x,int y)//建新图上的边
{
edge[++sum].to = y;
edge[sum].net = hed[x];
hed[x] = sum;
}
void tarjain(int x,int fa)//缩点
{
dfn[x] = low[x] = num++;
sta[++topp] = x; vis[x] = 1;
for(int i = head[x]; i; i = e[i].net)
{
int to = e[i].to;
if(to == fa) continue;//特判是不是联向他父亲得边
if(!dfn[to])
{
tarjain(to,x);
low[x] = min(low[x],low[to]);
}
else if(vis[to])
{
low[x] = min(low[x],dfn[to]);
}
}
if(dfn[x] == low[x])//求强联通分量
{
cnt++; int y;
do
{
y = sta[topp--];
//size[cnt]++;
shu[y] = cnt;
vis[y] = 0;
}while(x != y);
}
}
void get_tree(int x)//树剖第一遍DFS求重儿子
{
dep[x] = dep[fa[x]] + 1; size[x] = 1;
for(int i = hed[x]; i; i = edge[i].net)
{
int to = edge[i].to;
if(to == fa[x]) continue;
fa[to] = x;
get_tree(to);
size[x] += size[to];
if(size[to] > size[son[x]]) son[x] = to;
}
}
void dfs(int x,int topp)//树剖第二遍DFS求每条链的顶端
{
top[x] = topp;
if(son[x]) dfs(son[x],topp);
for(int i = hed[x]; i; i = edge[i].net)
{
int to = edge[i].to;
if(to == fa[x] || to == son[x]) continue;
dfs(to,to);
}
}
int lca(int x,int y)//树剖求LCA
{
while(top[x] != top[y])
{
if(dep[top[x]] < dep[top[y]]) swap(x,y);
x = fa[top[x]];
}
if(dep[x] < dep[y]) return x;
else return y;
}
void shuchu(int x)//二进制转化
{
int xx = 0;
for(; x; x>>=1)
{
xx++;
if(x & 1) t[xx] = 1;
else t[xx] = 0;
}
for (int i = xx; i >= 1; i--) printf("%d",t[i]);
printf("\n");
}
int main()
{
n = read(); m = read();
for(int i = 1; i <= m; i++)
{
u = read(); v = read();
add(u,v); add(v,u);
}
for(int i = 1; i <= n; i++)
{
if(!dfn[i]) tarjain(i,0);
}
for(int i = 1; i <= n; i++)//缩点
{
for(int j = head[i]; j; j = e[j].net)
{
int to = e[j].to;
if(shu[to] != shu[i])
{
add_(shu[i],shu[to]);
}
}
}
get_tree(1);
dfs(1,1);
T = read();
while(T--)
{
x = read(); y = read();
int Lca = lca(shu[x],shu[y]);
int ans = dep[shu[x]] + dep[shu[y]] - 2 * dep[Lca] + 1;//计算每个询问的答案
shuchu(ans);
}
return 0;
}