寒集训祭Day1圆方树
P3854
真的不是毒瘤题?
其实只需要把原图的每一个点对应为圆点,每一个点双对应为方点,建立圆方树,然后就按题目要求操作即可
#include <bits/stdc++.h>
using namespace std;
const int maxn = 40005;
int n, m, q, dfn[maxn], low[maxn], cnt, t;
vector<int> p[maxn], tr[maxn];
stack<int> s;
void tarjan(int u)
{
dfn[u] = low[u] = ++t;
s.push(u);
for (int v : p[u])
{
if (!dfn[v])
{
tarjan(v);
low[u] = min(low[u], low[v]);
if (dfn[u] == low[v])
{
++cnt;
while (s.top() != v)
{
tr[cnt].push_back(s.top());
tr[s.top()].push_back(cnt);
s.pop();
}
s.pop();
tr[cnt].push_back(v);
tr[v].push_back(cnt);
tr[cnt].push_back(u);
tr[u].push_back(cnt);
}
}
else
{
low[u] = min(low[u], dfn[v]);
}
}
}
int fa[maxn], siz[maxn], top[maxn], son[maxn], dep[maxn];
void dfs1(int u, int f)
{
fa[u] = f;
dep[u] = dep[f] + 1;
siz[u] = 1;
for (int v : tr[u])
{
if (v == f) continue;
dfs1(v, u);
siz[u] += siz[v];
if (siz[son[u]] < siz[v]) son[u] = v;
}
}
void dfs2(int u, int f)
{
top[u] = f;
if (!son[u]) return;
dfs2(son[u], f);
for (int v : tr[u])
{
if (v == son[u] || v == fa[u]) continue;
dfs2(v, v);
}
}
int lca(int x, int y)
{
while (top[x] != top[y])
{
if (dep[top[x]] < dep[top[y]]) swap(x, y);
x = fa[top[x]];
}
return dep[x] < dep[y] ? x : y;
}
int dis(int x, int y)
{
return dep[x] + dep[y] - (dep[lca(x, y)] << 1);
}
bool test(int u, int x, int v)
{
if (dis(u, x) + dis(x, v) == dis(u, v)) return true;
else return false;
}
void solve()
{
cin >> n >> m;
int u, v, x;
cnt = n;
for (int i = 1; i <= m; i++)
{
cin >> u >> v;
p[u].push_back(v);
p[v].push_back(u);
}
tarjan(1);
dfs1(1, 0);
dfs2(1, 1);
cin >> q;
while (q--)
{
cin >> u >> v >> x;
cout << (test(u, x, v) ? "yes" : "no") << endl;
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
solve();
return 0;
}
题外话:代码是一个隔壁班的大佬给我调的
P4630
赛时真的没有想到可以用容斥,所以喜提0pts/bx
不难发现答案就是路径上可能经过的点数,将图缩成圆方树,圆点权值为 \(-1\) ,方点权值为点双大小,则某条路径上可能经过的点数就是圆方树上路径点权和。跑一遍dfs即可解决
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int maxn = 5e5 + 10;
int sizsum, n, m, low[maxn], dfn[maxn], vistime, siz[maxn], si[maxn], idx;
vector<int> v[maxn], tr[maxn];
stack<int> st;
void Tarjan(int u, int fa)
{
low[u] = dfn[u] = ++vistime;
st.push(u);
si[u] = -1;
sizsum++;
for (int i = 0; i < (int)v[u].size(); i++)
{
if (!dfn[v[u][i]])
{
Tarjan(v[u][i], u);
low[u] = min(low[u], low[v[u][i]]);
if (low[v[u][i]] >= dfn[u])
{
idx++;
tr[idx].push_back(u);
tr[u].push_back(idx);
si[idx]++;
int vv;
do
{
vv = st.top();
st.pop();
tr[idx].push_back(vv);
tr[vv].push_back(idx);
si[idx]++;
} while (vv != v[u][i]);
}
}
else
{
low[u] = min(low[u], dfn[v[u][i]]);
}
}
}
int ans = 0;
void dfs(int u, int fa)
{
siz[u] = (u <= n);
int cnt = 0;
for (int i = 0; i < (int)tr[u].size(); i++)
{
if (tr[u][i] == fa) continue;
dfs(tr[u][i], u);
cnt += 1LL * siz[tr[u][i]] * siz[u];
siz[u] += siz[tr[u][i]];
}
cnt += 1LL * siz[u] * (sizsum - siz[u]);
cnt <<= 1;
ans += 1LL * si[u] * cnt;
}
signed main()
{
cin >> n >> m;
idx = n;
for (int i = 1; i <= m; i++)
{
int ui, vi;
cin >> ui >> vi;
v[ui].push_back(vi);
v[vi].push_back(ui);
}
for (int i = 1; i <= n; i++)
{
if (!dfn[i])
{
sizsum = 0;
Tarjan(i, 0);
dfs(i, 0);
}
}
cout << ans;
return 0;
}
P10140
赛时写了一个弱智 \(O(n^3)\) ,赛后听教练一讲直接通透了,教练思路比较奇妙,与这篇题解思路差不多,题解
代码还没写出来/bx
P10932
本题保证每条边仅在一个环中,属于“仙人掌图”。原图中的点为圆点,每个环建一个方点,环上圆点与方点连边权为环上该点到某起点的最短距,环上两点最短路径需取两种方向的最小值。
预处理圆方树后,用倍增求LCA,若LCA为圆点,答案为圆方树上距离;若为方点,则需在环上计算两方向的最小值。
复杂度\(O(N+Q\log N)\),通过建方点巧妙将环转化为树结构,高效处理环上最短路径。
P4320
全糖题,不会的可以重新学圆方树了
(2.22 操nm,被输入输出单杀了)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6+10;
vector<int> g[N], ng[N];
int dfn[N], low[N], T, stk[N], top, cnt;
int f[N][21], dep[N];
int n, m, q;
void tarjan(int u, int fr) {
dfn[u] = low[u] = ++T;
stk[++top] = u;
for(int v : g[u]) {
if(v == fr) continue;
if(dfn[v] == 0) {
tarjan(v, u);
low[u] = min(low[u], low[v]);
if(low[v] >= dfn[u]) {
++cnt;
int x;
do {
x = stk[top--];
ng[x].push_back(cnt);
ng[cnt].push_back(x);
} while(x != v);
ng[u].push_back(cnt);
ng[cnt].push_back(u);
}
} else {
low[u] = min(low[u], dfn[v]);
}
}
}
void dfs(int u, int fr) {
dep[u] = dep[fr] + 1;
f[u][0] = fr;
for(int i = 1; i <= 20; i++)
f[u][i] = f[f[u][i-1]][i-1];
for(int v : ng[u])
if(v != fr)
dfs(v, u);
}
int lca(int u, int v) {
if(dep[u] < dep[v]) swap(u, v);
for(int j = 20; j >= 0; j--)
if(dep[f[u][j]] >= dep[v])
u = f[u][j];
if(u == v) return u;
for(int j = 20; j >= 0; j--)
if(f[u][j] != f[v][j])
u = f[u][j], v = f[v][j];
return f[u][0];
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> m;
int u, v;
for(int i = 1; i <= m; i++) {
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
cnt = n;
tarjan(1, 0);
dfs(1, 0);
cin >> q;
while(q--) {
scanf("%lld%lld" , &u , &v);
int l = lca(u, v);
int ans = (dep[u] + dep[v] - 2 * dep[l] + 2) / 2;
printf("%lld\n" , ans);
}
return 0;
}

浙公网安备 33010602011771号