支配树题集
$$支配树题集$$
1.DAG上的支配树
①洛谷P2597
传送门
答案就是支配树上的节点子树大小-1
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 1e5+7;
int n,degree[MAXN],depth[MAXN],par[MAXN][20],ans[MAXN];
vector<int> G[MAXN],node,from[MAXN],GG[MAXN];
void toposort(){
queue<int> que;
for(int i = 1; i <= n; i++) if(degree[i]==0){
G[0].emplace_back(i);
from[i].emplace_back(0);
degree[i]++;
}
que.push(0);
while(!que.empty()){
int u = que.front();
que.pop();
for(int v : G[u]){
degree[v]--;
if(degree[v]==0){
node.emplace_back(v);
que.push(v);
}
}
}
}
int LCA(int u, int v){
if(depth[u]<depth[v]) swap(u,v);
for(int i = 0; i < 20; i++) if((depth[u]-depth[v])&(1<<i)) u = par[u][i];
if(u==v) return u;
for(int i = 19; i >= 0; i--) if(par[u][i]!=par[v][i]){
u = par[u][i];
v = par[v][i];
}
return par[u][0];
}
void build(){
for(int u : node){
par[u][0] = from[u][0];
for(int v : from[u]) par[u][0] = LCA(par[u][0],v);
depth[u] = depth[par[u][0]]+1;
for(int i = 1; par[u][i-1]; i++) par[u][i] = par[par[u][i-1]][i-1];
GG[par[u][0]].emplace_back(u);
}
}
void dfs(int u, int f){
ans[u] = 1;
for(int v : GG[u]){
if(v==f) continue;
dfs(v,u);
ans[u] += ans[v];
}
}
int main(){
scanf("%d",&n);
for(int i = 1; i <= n; i++){
int x;
while(scanf("%d",&x)&&x!=0){
G[x].emplace_back(i);
from[i].emplace_back(x);
degree[i]++;
}
}
toposort();
build();
dfs(0,0);
for(int i = 1; i <= n; i++) printf("%d\n",ans[i]-1);
return 0;
}
②codeforces 757F
传送门
先求最短路,然后把所有处在最短路上的边保留,以s为根节点建支配树,答案就是根节点外子树大小的最大值
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
typedef int_fast64_t LL;
const int MAXN = 2e5+7;
int n,m,s,degree[MAXN],depth[MAXN],par[MAXN][20],sz[MAXN],ans;
LL dist[MAXN];
vector<pair<int,int>> G[MAXN];
vector<int> reG[MAXN],node,from[MAXN],GG[MAXN];
void Dijkstra(){
memset(dist,0x3f,sizeof(dist));
dist[s] = 0;
priority_queue<pair<LL,int>,vector<pair<LL,int>>,greater<pair<LL,int>>> que;
que.push(make_pair(dist[s],s));
while(!que.empty()){
auto tp = que.top();
que.pop();
int u = tp.second;
LL d = tp.first;
if(dist[u]!=d) continue;
for(auto e : G[u]){
if(dist[u]+e.second<dist[e.first]){
dist[e.first] = dist[u] + e.second;
que.push(make_pair(dist[e.first],e.first));
}
}
}
}
void rebuild(){
for(int u = 1; u <= n; u++){
for(auto e : G[u]){
if(dist[u]+e.second==dist[e.first]){
reG[u].emplace_back(e.first);
from[e.first].emplace_back(u);
degree[e.first]++;
}
}
}
}
void toposort(){
queue<int> que;
que.push(s);
while(!que.empty()){
int u = que.front();
que.pop();
for(int v : reG[u]){
degree[v]--;
if(!degree[v]){
node.push_back(v);
que.push(v);
}
}
}
}
int LCA(int u, int v){
if(depth[u]<depth[v]) swap(u,v);
for(int i = 0; i < 20; i++) if((depth[u]-depth[v])&(1<<i)) u = par[u][i];
if(u==v) return u;
for(int i = 19; i >= 0; i--) if(par[u][i]!=par[v][i]){
u = par[u][i];
v = par[v][i];
}
return par[u][0];
}
void buildtree(){
for(int u : node){
if(from[u].empty()) continue;
par[u][0] = from[u][0];
for(int v : from[u]) par[u][0] = LCA(par[u][0],v);
for(int i = 1; par[u][i-1]; i++) par[u][i] = par[par[u][i-1]][i-1];
GG[par[u][0]].emplace_back(u);
depth[u] = depth[par[u][0]] + 1;
}
}
void dfs(int u){
sz[u] = 1;
for(int v : GG[u]){
if(v==par[u][0]) continue;
dfs(v);
sz[u] += sz[v];
}
if(u!=s) ans = max(ans,sz[u]);
}
void input(){
scanf("%d %d %d",&n,&m,&s);
for(int i = 1; i <= m; i++){
int u,v,c;
scanf("%d %d %d",&u,&v,&c);
G[u].emplace_back(make_pair(v,c));
G[v].emplace_back(make_pair(u,c));
}
}
int main(){
input();
Dijkstra();
rebuild();
toposort();
buildtree();
dfs(s);
printf("%d\n",ans);
return 0;
}
2.一般图上的支配树
①洛谷P5180
传送门
模板题,先找出半支配点,然后重新建图,变成DAG支配树,以\(1\)为根建支配树,答案就是各点子树大小
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5+7;
int n,m,dfn[MAXN],idx[MAXN],num,depth[MAXN],anc[MAXN],semi[MAXN],root[MAXN],msemi[MAXN],degree[MAXN];
int par[MAXN][20],sz[MAXN];
vector<int> G[MAXN],from[MAXN],newG[MAXN],node,reG[MAXN],refrom[MAXN];
void tarjan(int u){
dfn[u] = ++num;
idx[num] = u;
for(int v : G[u]){
if(dfn[v]) continue;
tarjan(v);
anc[v] = u;
newG[u].emplace_back(v);
refrom[v].emplace_back(u);
degree[v]++;
}
}
int findroot(int u){
if(u!=root[u]){
int rt = root[u];
root[u] = findroot(root[u]);
msemi[u] = min(msemi[rt],msemi[u]);
}
return root[u];
}
void calsemi(){
for(int i = 1; i <= n; i++){
root[i] = i;
msemi[i] = dfn[i];
}
for(int i = n; i >= 2; i--){
int u = idx[i];
if(!u) continue;
int tar = n;
for(int v : from[u]){
if(!dfn[v]) continue;
if(dfn[v]<dfn[u]) tar = min(tar,dfn[v]);
else{
findroot(v);
tar = min(tar,msemi[v]);
}
}
semi[u] = idx[tar];
msemi[u] = tar;
root[u] = anc[u];
newG[semi[u]].emplace_back(u);
refrom[u].emplace_back(semi[u]);
degree[u]++;
}
}
void toposort(){
queue<int> que;
que.push(1);
while(!que.empty()){
int u = que.front();
que.pop();
for(int v : newG[u]){
degree[v]--;
if(degree[v]==0){
node.emplace_back(v);
que.push(v);
}
}
}
}
int LCA(int u, int v){
if(depth[u]<depth[v]) swap(u,v);
for(int i = 0; depth[u]-depth[v]; i++) if((depth[u]-depth[v])&(1<<i)) u = par[u][i];
if(u==v) return u;
for(int i = 19; i >= 0; i--) if(par[u][i]!=par[v][i]){
u = par[u][i];
v = par[v][i];
}
return par[u][0];
}
void rebuild(){
for(int u : node){
if(refrom[u].empty()) continue;
par[u][0] = refrom[u][0];
for(int v : refrom[u]) par[u][0] = LCA(par[u][0],v);
depth[u] = depth[par[u][0]] + 1;
for(int i = 1; par[u][i-1]; i++) par[u][i] = par[par[u][i-1]][i-1];
reG[par[u][0]].emplace_back(u);
}
}
void dfs(int u){
sz[u] = 1;
for(int v : reG[u]){
if(v==par[u][0]) continue;
dfs(v);
sz[u] += sz[v];
}
}
inline int read(){
int x = 0, f = 1;
char c = getchar();
while(c!='-'&&(c<'0'||c>'9')) c = getchar();
if(c=='-') f = -1,c = getchar();
while(c>='0'&&c<='9') x = x*10+c-'0', c = getchar();
return f*x;
}
void input(){
n = read(), m = read();
for(int i = 1; i <= m; i++){
int u = read(), v = read();
G[u].emplace_back(v);
from[v].emplace_back(u);
}
}
void output(){
for(int i = 1; i <= n; i++) printf("%d ",sz[i]);
puts("");
}
int main(){
input();
tarjan(1);
calsemi();
toposort();
rebuild();
dfs(1);
output();
return 0;
}
②HDU4694
传送门
上模板,答案就是当前点到根路径上的点标和
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 5e4+7;
int n,m,num,dfn[MAXN],idx[MAXN],anc[MAXN],root[MAXN],semi[MAXN],msemi[MAXN],degree[MAXN],par[MAXN][20],depth[MAXN],ans[MAXN];
vector<int> G[MAXN],from[MAXN],node,newG[MAXN],rfrom[MAXN],rG[MAXN];
void init(){
for(int i = 1; i <= n; i++){
G[i].clear();
newG[i].clear();
from[i].clear();
rfrom[i].clear();
dfn[i] = idx[i] = degree[i] = 0;
rG[i].clear();
memset(par[i],0,sizeof(par[i]));
ans[i] = 0;
}
num = 0;
node.clear();
}
void tarjan(int u){
dfn[u] = ++num;
idx[num] = u;
for(int v : G[u]){
if(dfn[v]) continue;
tarjan(v);
anc[v] = u;
newG[u].emplace_back(v);
rfrom[v].emplace_back(u);
degree[v]++;
}
}
int findroot(int u){
if(u!=root[u]){
int rt = root[u];
root[u] = findroot(root[u]);
msemi[u] = min(msemi[u],msemi[rt]);
}
return root[u];
}
void calsemi(){
for(int i = 1; i <= n; i++){
root[i] = i;
msemi[i] = dfn[i];
}
for(int i = n; i >= 2; i--){
if(!idx[i]) continue;
int u = idx[i];
int tar = n;
for(int v : from[u]){
if(!dfn[v]) continue;
if(dfn[v]<dfn[u]) tar = min(tar,dfn[v]);
else{
findroot(v);
tar = min(tar,msemi[v]);
}
}
semi[u] = idx[tar];
msemi[u] = tar;
root[u] = anc[u];
newG[semi[u]].emplace_back(u);
rfrom[u].emplace_back(semi[u]);
degree[u]++;
}
}
void toposort(){
queue<int> que;
que.push(n);
while(!que.empty()){
int u = que.front();
que.pop();
for(int v : newG[u]){
degree[v]--;
if(!degree[v]){
node.emplace_back(v);
que.push(v);
}
}
}
}
int LCA(int u, int v){
if(depth[u]<depth[v]) swap(u,v);
for(int i = 0; i < 20; i++) if((depth[u]-depth[v])&(1<<i)) u = par[u][i];
if(u==v) return u;
for(int i = 19; i >= 0; i--) if(par[u][i]!=par[v][i]){
u = par[u][i];
v = par[v][i];
}
return par[u][0];
}
void buildtree(){
for(int u : node){
if(rfrom[u].empty()) continue;
par[u][0] = rfrom[u][0];
for(int v : rfrom[u]) par[u][0] = LCA(par[u][0],v);
depth[u] = depth[par[u][0]] + 1;
rG[par[u][0]].emplace_back(u);
for(int i = 1; par[u][i-1]; i++) par[u][i] = par[par[u][i-1]][i-1];
}
}
void dfs(int u, int sum){
ans[u] = sum;
for(int v : rG[u]) dfs(v,sum+v);
}
void input(){
for(int i = 1; i <= m; i++){
int u,v;
scanf("%d %d",&u,&v);
G[u].emplace_back(v);
from[v].emplace_back(u);
}
}
void output(){
for(int i = 1; i <= n; i++) printf(i==n?"%d":"%d ",ans[i]);
puts("");
}
int main(){
while(scanf("%d %d",&n,&m)!=EOF){
init();
input();
tarjan(n);
calsemi();
toposort();
buildtree();
dfs(n,n);
output();
}
return 0;
}
③2014-2015 ACM-ICPC, NEERC, Southern Subregional Contest L useful road
传送门
以\(1\)为根建支配树,检查所有边是不是支配树上的返祖边,如果是,说明必然两次经过祖先节点,路径不是useful road
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 2e5+7;
int n,m,dfn[MAXN],idx[MAXN],num,degree[MAXN],depth[MAXN],par[MAXN][20],root[MAXN],semi[MAXN],msemi[MAXN];
pair<int,int> edge[MAXN];
vector<int> G[MAXN],from[MAXN],rG[MAXN],rfrom[MAXN],newG[MAXN],node;
void init(){
for(int i = 1; i <= n; i++){
G[i].clear();
from[i].clear();
rG[i].clear();
rfrom[i].clear();
newG[i].clear();
memset(par[i],0,sizeof(par[i]));
dfn[i] = idx[i] = degree[i] = 0;
}
num = 0;
node.clear();
}
void tarjan(int u){
dfn[u] = ++num;
idx[num] = u;
for(int v : G[u]){
if(dfn[v]) continue;
tarjan(v);
rG[u].emplace_back(v);
rfrom[v].emplace_back(u);
degree[v]++;
}
}
int findroot(int u){
if(u!=root[u]){
int rt = root[u];
root[u] = findroot(root[u]);
msemi[u] = min(msemi[u],msemi[rt]);
}
return root[u];
}
void calsemi(){
for(int i = 1; i <= n; i++){
root[i] = i;
msemi[i] = dfn[i];
}
for(int i = n; i >= 2; i--){
if(!idx[i]) continue;
int u = idx[i],tar = n;
for(int v : from[u]){
if(!dfn[v]) continue;
if(dfn[v]<dfn[u]) tar = min(tar,dfn[v]);
else{
findroot(v);
tar = min(tar,msemi[v]);
}
}
semi[u] = idx[tar];
msemi[u] = tar;
root[u] = rfrom[u][0];
rG[semi[u]].emplace_back(u);
rfrom[u].emplace_back(semi[u]);
degree[u]++;
}
}
void toposort(){
queue<int> que;
que.push(1);
while(!que.empty()){
int u = que.front();
que.pop();
for(int v : rG[u]){
degree[v]--;
if(!degree[v]){
node.emplace_back(v);
que.push(v);
}
}
}
}
int LCA(int u, int v){
if(depth[u]<depth[v]) swap(u,v);
for(int i = 0; i < 20; i++) if((depth[u]-depth[v])&(1<<i)) u = par[u][i];
if(u==v) return u;
for(int i = 19; i >= 0; i--) if(par[u][i]!=par[v][i]){
u = par[u][i];
v = par[v][i];
}
return par[u][0];
}
void buildtree(){
for(int u : node){
if(rfrom[u].empty()) continue;
par[u][0] = rfrom[u][0];
for(int v : rfrom[u]) par[u][0] = LCA(par[u][0],v);
depth[u] = depth[par[u][0]] + 1;
newG[par[u][0]].emplace_back(u);
for(int i = 1; par[u][i-1]; i++) par[u][i] = par[par[u][i-1]][i-1];
}
}
int main(){
while(scanf("%d %d",&n,&m)!=EOF){
init();
for(int i = 1; i <= m; i++){
scanf("%d %d",&edge[i].first,&edge[i].second);
from[edge[i].second].emplace_back(edge[i].first);
G[edge[i].first].emplace_back(edge[i].second);
}
tarjan(1);
calsemi();
toposort();
buildtree();
vector<int> vec;
for(int i = 1; i <= m; i++){
if(!dfn[edge[i].first]||!dfn[edge[i].second]) continue;
int lca = LCA(edge[i].first,edge[i].second);
if(lca!=edge[i].second) vec.emplace_back(i);
}
printf("%d\n",vec.size());
for(int x : vec) printf("%d ",x);
puts("");
}
return 0;
}