支配树 2020-07-15
view
- 过了洛谷支配树的板子
- 过了HDU4694 ImportantSister(支配树裸题)
- 过了wmd支配树课件的习题CodeChef - GRAPHCNT
- 开始写支配树的笔记和总结(应该会咕很久)
detail
luoguP5180
事项
- 不要把dfn[x]的比较写成x的比较 , 不要把sdom[x]的比较写成x的比较。
code
#include <bits/stdc++.h>
#define endl '\n'
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define per(i,a,b) for(int i=a;i>=(b);--i)
#define repp(i,a,b) for(int i=a;i<(b);++i)
#define perr(i,a,b) for(int i=a;i>(b);--i)
#define pb push_back
#define rz resize
#define VI vector<int>
using namespace std;
vector< VI > G, _G, GDOM;
VI dfn, idfn, fa;
int dfs_clock = 1;
void dfs(int pre, int cur) {
idfn[dfs_clock] = cur;
dfn[cur] = dfs_clock++;
fa[cur] = pre;
for(int ver : G[cur])
if(!dfn[ver])
dfs(cur, ver);
}
inline int minpt(int u, int v) {
return dfn[u] < dfn[v] ? u : v;
}
VI par, dat, sdom, idom;
vector< VI > buc;
int eval(int cur) {
if(par[cur] == cur) return cur;
int anc = eval(par[cur]);
dat[cur] = dfn[sdom[dat[cur]]] < dfn[sdom[dat[par[cur]]]] ? dat[cur] : dat[par[cur]];
return par[cur] = anc;
}
void lengauer_tarjan(int n) {
for(int i = n; i; --i) sdom[i] = i, par[i] = i, dat[i] = i;
for(int i = n; i > 1; --i) {
int cur = idfn[i];
for(int ver : _G[cur]) {
if(!dfn[ver]) continue;
eval(ver);
sdom[cur] = minpt(sdom[cur], sdom[dat[ver]]);
}
par[cur] = fa[cur]; dat[cur] = sdom[cur];
buc[sdom[cur]].pb(cur);
for(int ver : buc[fa[cur]]) {
eval(ver);
if(sdom[dat[ver]] == fa[cur]) idom[ver] = sdom[ver];
else idom[ver] = dat[ver];
}
buc[fa[cur]].clear();
}
rep(i,1,n) {
int cur = idfn[i];
if(idom[cur] != sdom[cur]) idom[cur] = idom[idom[cur]];
}
}
VI ans;
void work(int cur) {
ans[cur] = 1;
for(int ver : GDOM[cur]) {
work(ver);
ans[cur] += ans[ver];
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m, u, v;
cin >> n >> m;
G.rz(n + 1); _G.rz(n + 1); GDOM.rz(n + 1); dfn.rz(n + 1); idfn.rz(n + 1); fa.rz(n + 1); par.rz(n + 1); dat.rz(n + 1); sdom.rz(n + 1); idom.rz(n + 1); buc.rz(n + 1); ans.rz(n + 1);
while(m--) {
cin >> u >> v;
G[u].pb(v);
_G[v].pb(u);
}
dfs(0, 1);
lengauer_tarjan(n);
rep(i,1,n) GDOM[idom[i]].pb(i);
work(1);
rep(i,1,n) cout << ans[i] << ' ';
cout << endl;
return 0;
}
HDU4694 Important Sister
事项
- 注意并查集中保存的是sdom[x]最小的点的编号而不是sdom的最小值。
- 有些点在流图中不可达 , 所以支配树中点的个数应为dfs后dfs_clock - 1。
- 初始化sdom[x] = par[x] = dat[x] = x;
- 不要把并查集的par[]和dfs树中的fa[]搞混淆
- 不要忘记清空buc[]
- 一个点求完在并查集中挂fa在buc中挂sdom , 但是弹出是弹出fa
- r无支配点 , 记idom[r] = 0
- 最后一步调整的过程为dfn升序
code
#include <bits/stdc++.h>
using namespace std;
typedef vector< vector<int> > Graph;
const int maxn = 56789;
Graph G, _G, DOM;
vector< vector<int> > buc;
int dfn[maxn], idfn[maxn], fa[maxn], dfs_clock = 1;
int par[maxn], dat[maxn];
int sdom[maxn], idom[maxn];
void dfs(int pre, int cur) {
idfn[dfs_clock] = cur; dfn[cur] = dfs_clock++;
fa[cur] = pre;
for(int ver : G[cur]) if(!dfn[ver]) dfs(cur, ver);
}
int find(int x) {
if(par[x] == x) return x;
int p = find(par[x]);
if(dfn[sdom[dat[par[x]]]] < dfn[sdom[dat[x]]]) dat[x] = dat[par[x]];
return par[x] = p;
}
inline int eval(int x) { find(x); return dat[x]; }
void lengauer_tarjan(int n, int r) {
memset(dfn, 0, sizeof(dfn));
memset(idfn, 0, sizeof(dfn));
memset(fa, 0, sizeof(fa));
memset(idom, 0, sizeof(idom));
dfs_clock = 1;
dfs(0, r);
--dfs_clock;
for(int i = 1; i <= n; ++i) par[i] = dat[i] = sdom[i] = i;
for(int i = dfs_clock; i > 1; --i) {
int cur = idfn[i];
for(int ver : _G[cur]) {
if(!dfn[ver]) continue;
int ev = eval(ver);
if(dfn[sdom[cur]] > dfn[sdom[ev]]) sdom[cur] = sdom[ev];
}
par[cur] = fa[cur]; buc[sdom[cur]].push_back(cur);
for(int ver : buc[fa[cur]]) {
if(sdom[eval(ver)] == fa[cur]) idom[ver] = fa[cur];
else idom[ver] = eval(ver);
}
buc[fa[cur]].clear();
}
for(int i = 2; i <= dfs_clock; i++) {
int cur = idfn[i];
if(idom[cur] != sdom[cur]) idom[cur] = idom[idom[cur]];
}
idom[r] = 0;
}
long long ans[maxn];
void work(int pre, int cur) {
ans[cur] = cur + ans[pre];
for(int ver : DOM[cur])
work(cur, ver);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int n, m, r;
while(cin >> n >> m) {
r = n;
G.clear(); G.resize(n + 1);
_G.clear(); _G.resize(n + 1);
DOM.clear(); DOM.resize(n + 1);
buc.clear(); buc.resize(n + 1);
int u, v;
while(m--) {
cin >> u >> v;
G[u].push_back(v);
_G[v].push_back(u);
}
lengauer_tarjan(n, r);
for(int i = 1; i <= n; ++i) {
DOM[idom[i]].push_back(i);
}
memset(ans, 0, sizeof(ans));
work(0, r);
for(int i = 1; i < n; ++i) cout << ans[i] << ' ';
cout << ans[n] << endl;
}
return 0;
}
CodeChef - GRAPHCNT
分析
原问题可转化为求支配树上lca = r的有序点对数量和
另一种count法挂了,没搞清楚为什么qaq
事项
- 大于号方向注意不要搞反
code
#include <bits/stdc++.h>
#define pb push_back
#define rz resize
using namespace std;
typedef vector<int> VI;
typedef vector<VI> Graph;
typedef long long ll;
const int maxn = 123456;
Graph G, _G, DOM;
VI buc[maxn];
int dfn[maxn], idfn[maxn], fa[maxn], dfs_clock;
int par[maxn], dat[maxn];
int sdom[maxn], idom[maxn];
void dfs(int pre, int cur) {
idfn[dfs_clock] = cur; dfn[cur] = dfs_clock++;
fa[cur] = pre;
for(int ver : G[cur]) if(!dfn[ver]) dfs(cur, ver);
}
int find(int x) {
if(par[x] == x) return x;
int p = find(par[x]);
if(dfn[sdom[dat[x]]] > dfn[sdom[dat[par[x]]]]) dat[x] = dat[par[x]];
return par[x] = p;
}
int eval(int x) { find(x); return dat[x]; }
void lengauer_tarjan(int n, int r) {
dfs_clock = 1;
dfs(0, r);
--dfs_clock;
for(int i = 1; i <= n; ++i) par[i] = dat[i] = sdom[i] = i;
for(int i = n; i > 1; --i) {
int cur = idfn[i];
for(int ver : _G[cur]) {
if(!dfn[ver]) continue;
int ev = eval(ver);
if(dfn[sdom[cur]] > dfn[sdom[ev]]) sdom[cur] = sdom[ev];
}
buc[sdom[cur]].pb(cur); par[cur] = fa[cur];
for(int ver : buc[fa[cur]]) {
int ev = eval(ver);
if(sdom[ev] == fa[cur]) idom[ver] = fa[cur];
else idom[ver] = ev;
}
buc[fa[cur]].clear();
}
for(int i = 2; i <= dfs_clock; ++i) {
int cur = idfn[i];
if(idom[cur] != sdom[cur]) idom[cur] = idom[idom[cur]];
DOM[idom[cur]].pb(cur);
}
idom[r] = 0;
}
int siz[maxn];
ll ans = 0;
void work(int cur) {
siz[cur] = 1;
for(int ver : DOM[cur]) {
work(ver);
if(cur == 1) ans += 1ll * siz[cur] * siz[ver];
siz[cur] += siz[ver];
}
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int n, m, u, v;
cin >> n >> m;
G.rz(n + 1); _G.rz(n + 1); DOM.rz(n + 1);
while(m--) {
cin >> u >> v;
G[u].pb(v);
_G[v].pb(u);
}
lengauer_tarjan(n, 1);
work(1);
cout << ans << endl;
return 0;
}