P3573 [POI 2014] RAJ-Rally 做题记录
P3573 [POI 2014] RAJ-Rally 做题记录
Description
给定一个 \(n\) 个点 \(m\) 条边的有向无环图,每条边长度都是 \(1\)。
请找到一个点,使得删掉这个点后剩余的图中的最长路径最短。
\(2\le n\le5\times10^5\),\(1\le m\le10^6\)。
Solution
如果不删掉任何点,答案怎么求呢?
注意到给出的图是一个 DAG,那么我们建立一个超级源点 \(S\) 和一个超级汇点 \(T\),可以用正反两遍拓扑排序 \(O(n)\) 求出每个点的 \(dis(S\rightarrow i)\) 与 \(dis(i \rightarrow T)\)。
我们令 \(dis(S\rightarrow S)=dis(T\rightarrow T)=-1\),我们枚举所有边 \((u\rightarrow v)\),答案就是 \(dis(S\rightarrow u)+dis(v\rightarrow T)+1\) 的最大值。
如果我们删掉了一个点,答案又该怎么求呢?
我们在拓扑排序中处理出拓扑序。删掉点 \(x\) 之后,点被分为两部分:拓扑序在 \(x\) 之前的点集 \(X\),拓扑序在 \(x\) 之后的点集 \(Y\)。
那么答案就有三种情况:
- \(\max_{p\in X}(dis(S\rightarrow p))\);
- \(\max_{p\in Y}(dis(p\rightarrow T))\);
- \(\max_{p\in X,q\in Y} (dis(S\rightarrow p)+dis(q\rightarrow T)+1)\),\((p\rightarrow q)\in E\)。
由于 \(Y\) 中的点的拓扑序都在 \(X\) 中的点之后, 所以不存在一条边 \((u\rightarrow v)\),使得 \(u\in Y,v\in X\)。
前两种情况是好处理的,但第三种呢?
我们拿出拓扑序最小的点 \(u\),其答案为 \(\max_{p\ne u} (dis(p\rightarrow T))\)。
假如我们已经求出了拓扑序在 \([1,i-1]\) 中的点的答案。我们把 \(X,Y\) 两个集合维护出来,现在 \(X\) 中的点拓扑序都小于 \(i\),\(Y\) 中的点都大于等于 \(i\)。
我们把拓扑序为 \(i\) 的这个点 \(v\) 从 \(Y\) 中拿出来。删掉 \(v\) 之后,\(dis(v\rightarrow T)\) 和所有的 \(dis(S\rightarrow z)+dis(v\rightarrow T)+1 \ ((z\rightarrow v)\in E)\) 都作废了。剩余的最大值就是 \(v\) 的答案。
接着,我们把 \(v\) 加入到 \(X\) 中。\(dis(S\rightarrow v)\) 与所有的 \(dis(S\rightarrow v)+dis(z\rightarrow T)+1 \ ((v\rightarrow z)\in E)\) 生效。
我们需要一个支持插入、删除、查询最大值的数据结构。multiset 维护即可。
时间复杂度为 \(O((n+m)\log (n+m))\)。
int n,m;
struct Edge{
int to,nxt,tp;
}edge[M<<1];
int topo[N],head[N],tot,in[N],dis1[N],dis2[N];
void Add(int u,int v,int tp){
edge[++tot]={v,head[u],tp};
head[u]=tot;
}
signed main(){
read(n),read(m);
for(int i=1;i<=n;i++){
Add(0,i,0);
in[i]=1;
}
for(int i=1;i<=m;i++){
int u,v;
read(u),read(v);
Add(u,v,0);
Add(v,u,1);
in[v]++;
}
queue<int> q; q.push(0); tot=-1;
dis1[0]=-1;
while(q.size()){
int x=q.front(); q.pop();
topo[++tot]=x;
for(int i=head[x];i;i=edge[i].nxt){
if(edge[i].tp) continue;
int t=edge[i].to;
Ckmax(dis1[t],dis1[x]+1);
if(!(--in[t])) q.push(t);
}
}
for(int i=n;i;i--){
int x=topo[i];
for(int j=head[x];j;j=edge[j].nxt){
if(edge[j].tp) continue;
int t=edge[j].to;
Ckmax(dis2[x],dis2[t]+1);
}
}
multiset<int> s;
for(int i=1;i<=n;i++) s.insert(dis2[i]);
int ans=IINF,frm=0;
for(int i=1;i<=n;i++){
int x=topo[i];
for(int j=head[x];j;j=edge[j].nxt){
if(!edge[j].tp) continue;
int t=edge[j].to;
s.erase(s.find(dis2[x]+dis1[t]+1));
}
s.erase(s.find(dis2[x]));
if(s.size()){
int res=*s.rbegin();
if(res<ans) ans=res,frm=x;
}
s.insert(dis1[x]);
for(int j=head[x];j;j=edge[j].nxt){
if(edge[j].tp) continue;
int t=edge[j].to;
s.insert(dis1[x]+dis2[t]+1);
}
}
printf("%d %d\n",frm,ans);
return 0;
}

浙公网安备 33010602011771号