支配树
原图-->dfs图-->支配树
支配点:支配树中Y的祖先加上它自己 都是Y的支配点.去掉支配点使得在原图中源点无法到达Y(即必经点)
最近支配点:支配树中Y最近的祖先
半支配点:在dfs tree中可以通过非dfs tree中的边到达y的深度最小的节点x 为y的半支配点
Lengauer-Tarjan算法 O(nlogn)
利用并查集
1.dfs构建搜索树 计算出dfn
2.按时间戳从大到小的顺序算出每个节点的半支配点
3.按照时间戳从小到大的顺序把半支配点≠支配点的节点进行修正
HDU 4694
/*Huyyt*/ #include<bits/stdc++.h> #define mem(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; typedef unsigned long long ull; const double eps = 1e-8; const int dir[8][2] = {{0, 1}, {1, 0}, {0, -1}, { -1, 0}, {1, 1}, {1, -1}, { -1, -1}, { -1, 1}}; const int mod = 1e9 + 7, gakki = 5 + 2 + 1 + 19880611 + 1e9; const int MAXN = 5e5 + 5, MAXM = 2e5 + 5; const int MAXQ = 100010, INF = 1e9; /*int to[MAXM << 1], nxt[MAXM << 1], Head[MAXN], tot = 1; int TO[MAXM << 1], NXT[MAXM << 1], HEAD[MAXN], TOT = 1; ll cost[MAXM << 1]; struct node { int u, v; ll c; } edge[MAXM]; inline void addedge(int u, int v) { to[++tot] = v; nxt[tot] = Head[u]; //cost[tot] = c; Head[u] = tot; }*/ inline void read(int &v) { v = 0; char c = 0; int p = 1; while (c < '0' || c > '9') { if (c == '-') { p = -1; } c = getchar(); } while (c >= '0' && c <= '9') { v = (v << 3) + (v << 1) + c - '0'; c = getchar(); } v *= p; } int dfn[MAXN], idom[MAXN], sdom[MAXN]; int son[MAXN], id[MAXN], fa[MAXN], f[MAXN]; int ans[MAXN]; int cnt; int n, m; vector<int> yuantu[MAXN], fantu[MAXN], zhipei[MAXN]; int findfather(int x) { if (f[x] == x) { return x; } int y = findfather(f[x]); if (sdom[son[x]] > sdom[son[f[x]]]) { son[x] = son[f[x]]; } return f[x] = y; } void dfs(int u) { id[dfn[u] = ++cnt] = u; for (int v, i = 0; i < yuantu[u].size(); i++) { if (v = yuantu[u][i], !dfn[v]) { dfs(v), fa[dfn[v]] = dfn[u]; } } } inline void tarjan(int s) { for (int i = 1; i <= n; i++) { f[i] = sdom[i] = son[i] = fa[i] = i; dfn[i] = 0; } cnt = 0, dfs(s); int k, x; for (int i = cnt; i > 1; i--) { for (int v, j = 0; j < fantu[id[i]].size(); j++) { if (v = fantu[id[i]][j], dfn[v]) { findfather(k = dfn[v]), sdom[i] = sdom[i] < sdom[son[k]] ? sdom[i] : sdom[son[k]]; } } zhipei[sdom[i]].push_back(i), f[i] = x = fa[i]; for (int v, j = 0; j < zhipei[f[i]].size(); j++) { v = zhipei[f[i]][j], findfather(k = v), idom[k] = sdom[son[k]] < x ? son[k] : x; } zhipei[x].clear(); } for (int i = 2; i <= cnt; zhipei[id[idom[i]]].push_back(id[i]), i++) { if (idom[i] != sdom[i]) { idom[i] = idom[idom[i]]; } } } void mp(int u, int p) { ans[u] = p; for (int v, i = 0; i < zhipei[u].size(); i++) { v = zhipei[u][i], mp(v, p + v); } } int main() { ios_base::sync_with_stdio(0); cin.tie(0); int T; int u, v; //freopen("jqkout.txt", "w", stdout); while (scanf("%d %d", &n, &m) == 2) { for (int i = 0; i <= n; i++) { yuantu[i].clear(), fantu[i].clear(), zhipei[i].clear(); } for (int i = 1; i <= m; i++) { scanf("%d %d", &u, &v); yuantu[u].push_back(v), fantu[v].push_back(u); } tarjan(n); mp(n, n); for (int i = 1; i <= n; i++) { cout << ans[i]; if (i != n) { cout << " "; } else { cout << endl; } ans[i] = 0; } } return 0; }
Opencup Petrozavodsk Winter Training Camp 2018
Day 8: Saratov SU Contest, Tuesday, February 8, 2018
Problem L. Increasing Costs
求出支配树 支配树上的点在DAG中入度为1的话 那条边则为必经边
/*Huyyt*/ #include<bits/stdc++.h> #define mem(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; typedef unsigned long long ull; const double eps = 1e-8; const int dir[8][2] = {{0, 1}, {1, 0}, {0, -1}, { -1, 0}, {1, 1}, {1, -1}, { -1, -1}, { -1, 1}}; const int mod = 1e9 + 7, gakki = 5 + 2 + 1 + 19880611 + 1e9; const int MAXN = 1e6 + 5, MAXM = 1e6 + 5; const int MAXQ = 100010, INF = 1e9; int to[MAXM << 1], nxt[MAXM << 1], Head[MAXN], tot = 1; ll cost[MAXM << 1]; int preedge[MAXN]; int du[MAXN]; struct node { int u, v; ll c; } edge[MAXM << 1]; inline void addedge(int u, int v, ll c) { to[++tot] = v; nxt[tot] = Head[u]; cost[tot] = c; Head[u] = tot; } inline void read(int &v) { v = 0; char c = 0; int p = 1; while (c < '0' || c > '9') { if (c == '-') { p = -1; } c = getchar(); } while (c >= '0' && c <= '9') { v = (v << 3) + (v << 1) + c - '0'; c = getchar(); } v *= p; } int dp[MAXN]; ll dis[MAXN]; bool visit[MAXN]; priority_queue < pair<ll, int>, vector<pair<ll, int> >, greater<pair<ll, int> > >que; bool chose[MAXM]; int dfn[MAXN], idom[MAXN], sdom[MAXN]; int son[MAXN], id[MAXN], fa[MAXN], f[MAXN]; int ans[MAXN]; int cnt; int n, m; vector<int> yuantu[MAXN], fantu[MAXN], zhipei[MAXN]; int findfather(int x) { if (f[x] == x) { return x; } int y = findfather(f[x]); if (sdom[son[x]] > sdom[son[f[x]]]) { son[x] = son[f[x]]; } return f[x] = y; } void dfs(int u) { id[dfn[u] = ++cnt] = u; for (int v, i = 0; i < yuantu[u].size(); i++) { if (v = yuantu[u][i], !dfn[v]) { dfs(v), fa[dfn[v]] = dfn[u]; } } } inline void tarjan(int s) { for (int i = 1; i <= n; i++) { f[i] = sdom[i] = son[i] = fa[i] = i; dfn[i] = 0; } cnt = 0, dfs(s); int k, x; for (int i = cnt; i > 1; i--) { for (int v, j = 0; j < fantu[id[i]].size(); j++) { if (v = fantu[id[i]][j], dfn[v]) { findfather(k = dfn[v]), sdom[i] = sdom[i] < sdom[son[k]] ? sdom[i] : sdom[son[k]]; } } zhipei[sdom[i]].push_back(i), f[i] = x = fa[i]; for (int v, j = 0; j < zhipei[f[i]].size(); j++) { v = zhipei[f[i]][j], findfather(k = v), idom[k] = sdom[son[k]] < x ? son[k] : x; } zhipei[x].clear(); } for (int i = 2; i <= cnt; zhipei[id[idom[i]]].push_back(id[i]), i++) { if (idom[i] != sdom[i]) { idom[i] = idom[idom[i]]; } } } void get_ans(int u) { dp[u] = 1; for (int v, i = 0; i < zhipei[u].size(); i++) { v = zhipei[u][i]; //cout << u << " " << v << endl; get_ans(v); dp[u] += dp[v]; } if (du[u] == 1) { ans[preedge[u]] = dp[u]; } } int main() { ios_base::sync_with_stdio(0); cin.tie(0); int T; //freopen("jqkout.txt", "w", stdout); int u, v, c; read(n), read(m); for (int i = 1; i <= m; i++) { chose[i * 2] = chose[i * 2 - 1] = false; //scanf("%d %d %d", &u, &v, &c); read(u), read(v), read(c); edge[i * 2 - 1].u = u, edge[i * 2 - 1].v = v, edge[i * 2 - 1].c = c; edge[i * 2].u = v, edge[i * 2].v = u, edge[i * 2].c = c; addedge(u, v, c); addedge(v, u, c); } for (int i = 1; i <= n; i++) { visit[i] = false; dis[i] = LLONG_MAX; } dis[1] = 0; pair<ll, int> cnt; que.push(make_pair(0, 1)); while (!que.empty()) { cnt = que.top(); que.pop(); visit[cnt.second] = true; for (int i = Head[cnt.second]; i; i = nxt[i]) { int v = to[i]; if (visit[v]) { continue; } if (dis[v] > dis[cnt.second] + cost[i]) { dis[v] = dis[cnt.second] + cost[i]; que.push(make_pair(dis[v], v)); } } } // for (int i = 1; i <= n; i++) // { // cout << dis[i] << " "; // } // cout << endl; for (int i = 1; i <= 2 * m; i++) { if (dis[edge[i].v] == dis[edge[i].u] + edge[i].c) { //cout << edge[i].u << " " << edge[i].v << endl; chose[i] = true; yuantu[edge[i].u].push_back(edge[i].v); du[edge[i].v]++; preedge[edge[i].v] = (i + 1) / 2; fantu[edge[i].v].push_back(edge[i].u); } } tarjan(1); get_ans(1); for (int i = 1; i <= m; i++) { cout << ans[i] << endl; } return 0; }