P3573 [POI2014] RAJ-Rally
P3573 [POI2014] RAJ-Rally
[POI2014] RAJ-Rally
题面翻译
题目描述
给定一个 \(n\) 个点 \(m\) 条边的有向无环图,每条边长度都是 \(1\)。
请找到一个点,使得删掉这个点后剩余的图中的最长路径最短。
输入格式&&数据范围
第一行包含两个正整数 \(n\),\(m\)(\(2\le n\le5\times10^5\),\(1\le m\le10^6\)),表示点数、边数。
接下来 \(m\) 行每行包含两个正整数 \(a_i,b_i\)(\(1\le a_i,b_i\le n,a_i\ne b_i\)),表示 \(a_i\) 到 \(b_i\) 有一条边。
Solution:
是图论思维题捏 由于是 DAG 所以我们能直接拓扑,在$ O(n) $ 的时间内求出以每个点为起点/终点的最短路 \(dis_{st},dis_{ed}\)
具体的:我们在存图时存一张原图 \(E\) ,一张补图 \(e\) ,同时记录每个点的入度 \(d_u\)
然后我们就跑出这整个图的拓扑序列 \(V\)
对于\(dis_{ed}:\) 我们顺序遍历整个 \(V\) 然后统计当前节点 \(u\) 对于其在原图 \(E\) 上的邻居 \(v\) 的贡献
对于\(dis_{st}:\) 我们逆序遍历整个 \(V\) 然后统计当前节点 \(u\) 对于其在反图 \(e\) 上的邻居 \(v\) 的贡献
然后我们开始思考如何统计答案:
对于一个点,它可能产生三种贡献:
1.\(dis_{st_u}\)
2.\(dis_{ed_u}\)
3.\(dis_{ed_u}+dis_{st_v} (u,v) \in E\)
然后我们想要删除一个点的话只要删掉以上三种贡献就好了
那么我们这么维护呢?
我们维护一个类似可删优先队列的东西,并且我们只关注堆顶,并且只会查询 \(n\) 次。
虽然可删的优先队列并不存在,但是上述数据结构可以用两个优先队列 \(A,B\) 维护,\(A\) 维护所求堆, \(B\) 维护删除堆。对于每次询问,将所有 \(B\) 中的元素尽可能地从 \(A\) 中删除
然后这题就愉快的写完了
Code
#include<bits/stdc++.h>
const int N=1e6+5;
const int inf=1e9;
using namespace std;
inline int Max(int x,int y){return x > y ? x : y;}
inline int Min(int x,int y){return x < y ? x : y;}
vector<int> E[N],e[N];
int n,m,ans=inf,pos;
int V[N],dis_st[N],dis_ed[N],d[N];
queue<int> q;
struct Queue{
priority_queue<int> A,B;
void add(int x){A.push(x);}
void del(int x){B.push(x);}
void calc(){while(!A.empty()&&!B.empty()&&A.top()==B.top()){A.pop(),B.pop();}}
}Q;
void work()
{
cin>>n>>m;
for(int i=1,x,y;i<=m;i++)
{
scanf("%d%d",&x,&y);
E[x].emplace_back(y);
e[y].emplace_back(x);d[y]++;//out
}
for(int u=1;u<=n;u++)
{
if(!d[u])q.push(u);
}
while(!q.empty())
{
int u=q.front();q.pop();
V[++V[0]]=u;
for(auto v : E[u])if(--d[v]==0)q.push(v);
}
for(int i=1;i<=V[0];i++)
{
int u=V[i];
for(auto v : E[u])dis_ed[v]=Max(dis_ed[u]+1,dis_ed[v]);
}
for(int i=V[0];i;i--)
{
int u=V[i];
for(auto v : e[u])dis_st[v]=Max(dis_st[u]+1,dis_st[v]);
}
for(int i=1;i<=V[0];i++)Q.add(dis_st[i]);
for(int i=1;i<=V[0];i++)
{
int u=V[i];
Q.del(dis_st[u]);
for(auto v : e[u])Q.del(dis_ed[v]+1+dis_st[u]);
Q.calc();
int tmp=inf;
if(!Q.A.empty());
tmp=Q.A.top();
if(tmp<ans){ans=tmp,pos=u;}
for(auto v : E[u])Q.add(dis_ed[u]+1+dis_st[v]);
Q.add(dis_ed[u]);
}
printf("%d %d",pos,ans);
}
int main()
{
//freopen("P3573.in","r",stdin);freopen("P3573.out","w",stdout);
work();
return 0;
}

浙公网安备 33010602011771号