今天刚刚看了支配树,然而恕我太弱并没有看明白Lengauer-Tarjan算法,比较懵QAQ……不过我倒是学会了在DAG上构建支配树,就贴一道简单题过来吧O(∩_∩)O。
如何在DAG上求支配树?
有向无环图(DAG)里我们可以按照拓扑序构建支配树。
假设当前我们构造到拓扑序中第x个节点编号为v,那么此时支配树中已经有拓扑序第1~x-1个节点了。考虑所有能够直接到达v的节点,对于这些节点我们求出它们在支配树上的最近公共祖先Lca(所以要反向建一份图g2),这个点Lca就是点v在支配树上的父亲。
这样,支配树就建好了
用倍增求LCA的话,时间复杂度是O((n+m)log2n)。
题目链接:codeforces 757 F
题解:这个题的话,跑出最短路图,再在最短路图上建支配树,求最大的子树大小。
code(有参考网上神犇们的代码)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define inf 1LL<<60
using namespace std;
inline int read()
{
char c=getchar(); int num=0,f=1;
while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }
while (c<='9'&&c>='0') { num=num*10+c-'0'; c=getchar(); }
return num*f;
}
struct edge{
int to,ne;
long long val;
}e[600005],g1[600005],g2[600005];
//e[]是原图,g1[]是最短路图,g2[]和g1[]边的方向相反
int head[200005],head1[200005],head2[200005];
int que[4000005];
int d[200005],deep[200005],siz[200005];
int fa[200005][22];
int S,n,m,tot,tot1,tot2,ans;
bool vis[200005];
long long dis[200005];
void push(int x,int y,long long val)
{
e[++tot].to=y; e[tot].val=val; e[tot].ne=head[x]; head[x]=tot;
}
void push1(int x,int y)
{
g1[++tot1].to=y; g1[tot1].ne=head1[x]; head1[x]=tot1;
}
void push2(int x,int y)
{
g2[++tot2].to=y; g2[tot2].ne=head2[x]; head2[x]=tot2;
}
void spfa(int S)
{
for (int i=1;i<=n;i++) dis[i]=inf;
int h=0,t=1;
dis[S]=0,vis[S]=true,que[t]=S;
while (h<t)
{
int now=que[++h];
for (int i=head[now];i;i=e[i].ne)
{
int v=e[i].to;
if (dis[v]>dis[now]+e[i].val)
{
dis[v]=dis[now]+e[i].val;
if (!vis[v]) vis[v]=true,que[++t]=v;
}
}
vis[now]=false;
}
}
void dfs1(int now) //找出最短路图中的边
{
vis[now]=true;
for (int i=head[now];i;i=e[i].ne)
{
int v=e[i].to;
if (dis[v]==dis[now]+e[i].val)
{
push1(now,v);
push2(v,now);
d[v]++;
if (!vis[v]) dfs1(v);
}
}
}
void dfs2(int now) //求出支配树各节点的size,更新答案
{
siz[now]=1;
for (int i=head[now];i;i=e[i].ne)
{
int v=e[i].to;
dfs2(v);
siz[now]+=siz[v];
}
if (now!=S) ans=max(ans,siz[now]);
}
int lca(int x,int y) //倍增lca
{
if (x==y) return x;
if (deep[x]<deep[y]) swap(x,y);
for (int i=20;i>=0;i--)
if (deep[fa[x][i]]>=deep[y]) x=fa[x][i];
if (x==y) return x;
for (int i=20;i>=0;i--)
if (fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
void topsort() //拓扑排序
{
int h=0,t=1;
que[t]=S,deep[S]=1;
while (h<t)
{
int now=que[++h];
for (int i=head1[now];i;i=g1[i].ne)
{
int v=g1[i].to;
d[v]--;
if (!d[v])
{
que[++t]=v;
int Lca=0;
for (int j=head2[v];j;j=g2[j].ne)
{
int vv=g2[j].to;
if (!Lca) Lca=vv;
else Lca=lca(vv,Lca);
} //找到v的支配点Lca,把v点加入支配树里
push(Lca,v,0);
fa[v][0]=Lca,deep[v]=deep[Lca]+1;
for (int j=1;j<=20;j++) fa[v][j]=fa[fa[v][j-1]][j-1];
}
}
}
}
int main()
{
n=read(); m=read(); S=read();
for (int i=1;i<=m;i++)
{
int x=read(),y=read(),z=read();
push(x,y,(long long)z);
push(y,x,(long long)z);
}
spfa(S); dfs1(S); //求最短路图
memset(head,0,sizeof(head)),tot=0;
topsort(); dfs2(S); //建支配树
printf("%d\n",ans);
return 0;
}