bzoj3832[Poi2014]Rally(set,bitset,位運算,拓撲排序,堆,線段樹)
傳送門: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
:
思路神題
這題網上題解都是
的,加點科技就可以近乎
了
先講一下
的做法
考慮一個點的答案
先求出 表示以 為終點的最長鏈, 表示以 為起點的最長鏈
一條最長鏈肯定可以通過一條邊表示為
刪掉一個點的答案也可以表示為上面那個式子
不難發現一個點的答案可以被所有跨過他的邊更新
於是就可以考慮用拓撲排序來做
也就是對於一條邊
可以更新
的所有點(其中
表示的是
這個點在拓撲序中的標號)
用個
維護一下
對於邊
在
加入,
刪除
用堆和線段樹也可以維護
注意 只取 的情況
這個做法只要加一點點科技就可以近似
了
我們把一條邊的影響查分一下,用
存下來,再開個
存一下當前的合法解
搞一下就好了
程式碼:
#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;
}