Task:给定一个有向无环图,边的长度都为1。找到一个点使得删掉这个点后图中剩余的最长路径最短。(N<=500 000,M<=1 000 000)
Solution:
神一样的题…
将原图划分为两个集合
S,T
。一开始所有点都属于
T
,然后按照拓扑序逐步将
当然这样讲并不能懂,来看一看实际的做法:
- 拓扑dp处理出,表示到点的最长路,点出发的最长路。
- 首先把所有的都加入到堆中(所有点属于
T )按照拓扑序删点:
- 对于一个点 i :
- 删除
g[i]⋯⋯③ - 删除 f[j]+g[i]+1(j→i) 【入边】 ⋯⋯①
- 统计答案【取堆顶】
- 加入 f[i]+g[j]+1(i→j) 【出边】 ⋯⋯②
- 加入 f[i]⋯⋯④
从 ①→② 、 ③→④ 的过程中其实就是将拓扑序中前一个点的状态转移到当前点上的一个过程。
什么?你说堆不能删除?—来自某大神
#include<ctime> #include<cmath> #include<cstdio> #include<cctype> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #include<set> #include<map> #include<queue> #include<vector> #include<string> #define fi first #define se second #define ll long long #define ull unsigned long long #define lson (p<<1) #define rson (p<<1|1) #define mp make_pair #define pb push_back #define pll pair<ll,ll> #define pii pair<int,int> #define lowbit(x) ((x)&(-x)) #define siz(x) (int)((x).size()) using namespace std; inline void Rd(int &res){ char c;res=0; while(c=getchar(),c<'0'); do{ res=(res<<1)+(res<<3)+(c^48); }while(c=getchar(),c>='0'); } const int M=500005; struct Edge{int to,nxt;}Edge[M<<1],rEdge[M<<1]; int Head[M],rHead[M],tot,degree[M],rdegree[M],degree2[M],n; inline void Addedge(int a,int b){ Edge[++tot].to=b;Edge[tot].nxt=Head[a];Head[a]=tot;degree[b]++;degree2[b]++; rEdge[tot].to=a;rEdge[tot].nxt=rHead[b];rHead[b]=tot;rdegree[a]++; } int Q[M],f[M],g[M]; struct Heap{//可删除堆 priority_queue<int>q,p; void Push(int x){ q.push(x); } void Del(int x){ p.push(x); while(!q.empty()&&!p.empty()&&q.top()==p.top())q.pop(),p.pop(); } int Top(){ if(q.empty())return 0; else return q.top(); } }Heap; void Topo_pre(){ int L=0,R=-1; for(int i=1;i<=n;i++) if(degree[i]==0)Q[++R]=i; while(L<=R){ int now=Q[L++]; for(int i=Head[now];~i;i=Edge[i].nxt){ int to=Edge[i].to; degree[to]--; if(f[now]+1>f[to])f[to]=f[now]+1; if(degree[to]==0)Q[++R]=to; } } L=0,R=-1; for(int i=1;i<=n;i++) if(rdegree[i]==0)Q[++R]=i; while(L<=R){ int now=Q[L++]; for(int i=rHead[now];~i;i=rEdge[i].nxt){ int to=rEdge[i].to; rdegree[to]--; if(g[now]+1>g[to])g[to]=g[now]+1; if(rdegree[to]==0)Q[++R]=to; } } } void Topo_solve(){ int L=0,R=-1,ans=n*2,id; for(int i=1;i<=n;i++) if(degree2[i]==0)Q[++R]=i; while(L<=R){ int now=Q[L++]; Heap.Del(g[now]); for(int j=rHead[now];~j;j=rEdge[j].nxt){ int to=rEdge[j].to; Heap.Del(f[to]+g[now]+1); } if(Heap.Top()<ans){ ans=Heap.Top(); id=now; } for(int j=Head[now];~j;j=Edge[j].nxt){ int to=Edge[j].to; Heap.Push(f[now]+g[to]+1); degree2[to]--; if(degree2[to]==0)Q[++R]=to; } Heap.Push(f[now]); } printf("%d %d\n",id,ans); } int main(){ memset(Head,-1,sizeof(Head)); memset(rHead,-1,sizeof(rHead)); int m; Rd(n),Rd(m); while(m--){ int a,b; Rd(a),Rd(b); Addedge(a,b); } Topo_pre(); for(int i=1;i<=n;i++)Heap.Push(g[i]); Topo_solve(); return 0; }