传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=3832
Description
给定一个N个点M条边的有向无环图,每条边长度都是1。
请找到一个点,使得删掉这个点后剩余的图中的最长路径最短。
Input
第一行包含两个正整数N,M(2<=N<=500 000,1<=M<=1 000 000),表示点数、边数。
接下来M行每行包含两个正整数A[i],Bi,表示A[i]到B[i]有一条边。
Output
包含一行两个整数x,y,用一个空格隔开,x为要删去的点,y为删除x后图中的最长路径的长度,如果有多组解请输出任意一组。
Sample Input
6 5
1 3
1 4
3 6
3 4
4 5
Sample Output
1 2
s
o
l
u
t
i
o
n
solution
solution:
思路神题
这题网上题解都是
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)的,加点科技就可以近乎
O
(
n
)
O(n)
O(n)了
先讲一下
n
l
o
g
n
nlogn
nlogn的做法
考虑一个点的答案
先求出 f i f_i fi表示以 i i i为终点的最长链, g i g_i gi表示以 i i i为起点的最长链
一条最长链肯定可以通过一条边表示为
f
i
+
g
j
+
1
f_i+g_j+1
fi+gj+1
删掉一个点的答案也可以表示为上面那个式子
不难发现一个点的答案可以被所有跨过他的边更新
于是就可以考虑用拓扑排序来做
也就是对于一条边
i
→
j
i\rightarrow j
i→j
可以更新
t
o
p
[
i
]
∼
t
o
p
[
j
]
top[i]\sim top[j]
top[i]∼top[j]的所有点(其中
t
o
p
[
x
]
top[x]
top[x]表示的是
x
x
x这个点在拓扑序中的标号)
用个
s
e
t
set
set维护一下
对于边
i
→
j
i\rightarrow j
i→j在
i
i
i加入,
j
j
j删除
用堆和线段树也可以维护
注意 a n s ans ans只取 f i , g i f_i,g_i fi,gi的情况
这个做法只要加一点点科技就可以近似
O
(
n
)
O(n)
O(n)了
我们把一条边的影响查分一下,用
v
e
c
t
o
r
vector
vector存下来,再开个
b
i
t
s
e
t
bitset
bitset存一下当前的合法解
搞一下就好了
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)代码:
#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define rept(i,x) for(int i = linkk[x],y = e[i].y;i;i = e[i].n,y = e[i].y)
#define reptt(i,x) for(int i = llinkk[x],y = ee[i].y;i;i = ee[i].n,y = ee[i].y)
#define P pair<int,int>
#define Pil pair<int,ll>
#define Pli pair<ll,int>
#define Pll pair<ll,ll>
#define pb push_back
#define pc putchar
#define mp make_pair
#define file(k) memset(k,0,sizeof(k))
#define ll long long
int rd()
{
int num = 0;char c = getchar();bool flag = true;
while(c < '0'||c > '9') {if(c == '-') flag = false;c = getchar();}
while(c >= '0' && c <= '9') num = num*10+c-48,c = getchar();
if(flag) return num;else return -num;
}
int n,m,S,T;
int linkk[501000],t,llinkk[501000],tt;
int f[501000],g[501000],ru[501000],chu[501000];
int q[501000],head,tail,tp[501000],tot;
struct node{int n,y;}e[2010000],ee[2010000];
inline int max(int a,int b){return a>b?a:b;}
void insert(int x,int y)
{
e[++t].y = y;e[t].n = linkk[x];linkk[x] = t;chu[x]++;
ee[++tt].y = x;ee[t].n = llinkk[y];llinkk[y] = tt;ru[y]++;
}
void init()
{
n = rd();m = rd();S = n+1;T = n+2;
rep(i,1,m)
{
int x = rd(),y = rd();
insert(x,y);
}
rep(i,1,n) insert(S,i),insert(i,T);
}
void topsort()
{
q[0] = S;
while(head<=tail)
{
int x = q[head++];
rept(i,x)
{
f[y] = max(f[y],f[x]+1);
ru[y]--;
if(ru[y] == 0) q[++tail] = y;
}
}
tot = tail-1;rep(i,1,tot) tp[i] = q[i];
head = tail = 0;q[0] = T;
while(head <= tail)
{
int x = q[head++];
reptt(i,x)
{
g[y] = max(g[y],g[x]+1);
chu[y]--;
if(chu[y] == 0) q[++tail] = y;
}
}
rep(i,1,n) f[i]--,g[i]--;
}
multiset<int>h;
int main()
{
init();
topsort();
int ans = n+2,k = 0;
rep(i,1,tot) h.insert(g[i]);
rep(i,1,tot)
{
int x = tp[i];
h.erase(h.lower_bound(g[x]));
reptt(i,x) if(y != S) h.erase(h.lower_bound(f[y]+g[x]+1));
multiset<int>::iterator it = h.end();
if(it != h.begin())
{
it--;
int tmp = (*it);
if(tmp < ans) ans = tmp,k = x;
}
rept(i,x) if(y != T) h.insert(f[x]+g[y]+1);
h.insert(f[x]);
}
printf("%d %d\n",k,ans);
return 0;
}