【bzoj 3832】 [Poi2014] Rally (权值线段树+拓扑排序)
3832: [Poi2014]Rally
Time Limit: 20 Sec Memory Limit: 128 MBSec Special JudgeSubmit: 160 Solved: 82
[Submit][Status][Discuss]
Description
An annual bicycle rally will soon begin in Byteburg. The bikers of Byteburg are natural long distance cyclists. Local representatives of motorcyclists, long feuding the cyclists, have decided to sabotage
the event.
There are intersections in Byteburg, connected with one way streets. Strangely enough, there are no cycles in the street network - if one can ride from intersection U to intersection V , then it
is definitely impossible to get from V to U.
The rally's route will lead through Byteburg's streets. The motorcyclists plan to ride their blazing machines in the early morning of the rally day to one intersection and completely block it. The
cyclists' association will then of course determine an alternative route but it could happen that this new route will be relatively short, and the cyclists will thus be unable to exhibit their remarkable endurance. Clearly, this is the motorcyclists' plan
- they intend to block such an intersection that the longest route that does not pass through it is as short as possible.
给定一个N个点M条边的有向无环图,每条边长度都是1。
请找到一个点,使得删掉这个点后剩余的图中的最长路径最短。
Input
In the first line of the standard input, there are two integers, N and M(2<=N<=500 000,1<=M<=1 000 000), separated by a single space, that specify the number of intersections and streets in Byteburg.
The intersections are numbered from to . The lines that follow describe the street network: in the -th of these lines, there are two integers, Ai, Bi(1<=Ai,Bi<=N,Ai<>Bi), separated by a single space, that signify that there is a one way street from the
intersection no. Ai to the one no. Bi.
第一行包含两个正整数N,M(2<=N<=500 000,1<=M<=1 000 000),表示点数、边数。
接下来M行每行包含两个正整数A[i],B[i](1<=A[i],B[i]<=N,A[i]<>B[i]),表示A[i]到B[i]有一条边。
Output
The first and only line of the standard output should contain two integers separated by a single space. The first of these should be the number of the intersection that the motorcyclists should block,
and the second - the maximum number of streets that the cyclists can then ride along in their rally. If there are many solutions, your program can choose one of them arbitrarily.
包含一行两个整数x,y,用一个空格隔开,x为要删去的点,y为删除x后图中的最长路径的长度,如果有多组解请输出任意一组。
Sample Input
6 5
1 3
1 4
3 6
3 4
4 5
1 3
1 4
3 6
3 4
4 5
Sample Output
1 2
HINT
Source
【题解】【权值线段树+拓扑排序】
【f[i]表示到点i的最长路,g[i]表示从点i出发的最长路。那么,经过点i最长路就是f[i]+1+g[i]】
【维护权值线段树】
【每次删点时,将经过该点的最长路删掉,再将从该点出发的最长路删掉,求出当前最大值后,还原整个图】
【为什么只用删去从该点出发的最长路呢?因为我们刚开始加入的时候就只加入了这个点之后的最长路。然后这时线段树中的最大值就是删去该点后的答案。那么一条最长路上的每条边能产生的最长路的值都是相同的,只删除这个点的,会不会在这条路径上的其他边的最大值会影响答案呢?其实是不会的,因为我们是按照拓扑序来搞的,他后面的点只加入了g[x],根本没有加入这条路径的信息。
然后再把这个点的出边所能产生的最长路加入线段树,并把f[i]加入线段树,因为如果之后再用该点更新答案的话,一定会选择到该点的最长路。之所以要把所有出边产生的最长路加入线段树,是因为在删除这个点之后的点的时候,可能会把这个点以后最长路破坏,所有需要把所有路径记录下来,保证可以通过所有可能的路径更新答案。】
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int a[1000010],nxt[1000010],p[500010],tt;
int a1[1000010],next[1000010],p1[500010],tt1;
int f[500010],g[500010],in[500010],out[500010],que[1000010];
int sum[4000010],maxn[4000010];
int n,m,ans=0x7fffffff,ansx;
inline void add(int x,int y)
{
tt++; a[tt]=y; nxt[tt]=p[x]; p[x]=tt;
}
inline void add1(int x,int y)
{
tt1++; a1[tt1]=y; next[tt1]=p1[x]; p1[x]=tt1;
}
inline void spfa()
{
int h,t; h=t=0;
for(int i=1;i<=n;++i)
if(!out[i]) que[++t]=i,g[i]=0;
while(h<t)
{
int u=que[++h],v=p1[u];
while(v!=-1)
{
if(g[a1[v]]<g[u]+1) g[a1[v]]=g[u]+1;
if(!(--out[a1[v]])) que[++t]=a1[v];
v=next[v];
}
}//SPFA计算从当前点出发的最长路
h=t=0;
for(int i=1;i<=n;++i)
if(!in[i]) que[++t]=i,f[i]=0;
while(h<t)
{
int u=que[++h],v=p[u];
while(v!=-1)
{
if(f[a[v]]<f[u]+1) f[a[v]]=f[u]+1;
if(!(--in[a[v]])) que[++t]=a[v];
v=nxt[v];
}
}//SPFA计算到当前点的最长路
}
void change(int now,int l,int r,int x,int v)
{
if(l==r)
{
sum[now]+=v;
if(sum[now]>0) maxn[now]=l;
else maxn[now]=-1,sum[now]=0;
return;
}
int mid=(l+r)>>1;
if(x<=mid) change((now<<1),l,mid,x,v);
else change((now<<1)|1,mid+1,r,x,v);
maxn[now]=max(maxn[(now<<1)],maxn[(now<<1)|1]);
return;
}//线段树
int main()
{
int i,j;
memset(p,-1,sizeof(p));
memset(p1,-1,sizeof(p1));
memset(nxt,-1,sizeof(nxt));
memset(next,-1,sizeof(next));
scanf("%d%d",&n,&m);
for(i=1;i<=m;++i)
{
int x,y;
scanf("%d%d",&x,&y);
in[y]++; out[x]++;//记录入度、出度
add(x,y); add1(y,x);//正、反向存图
}
spfa();
for(i=1;i<=n;++i) change(1,0,n,g[i],1);//初始线段树,将从当前点出发的最长路存入权值线段树
for(i=1;i<=n;++i)
{
int u=que[i];
for(j=p1[u];j!=-1;j=next[j]) change(1,0,n,g[u]+1+f[a1[j]],-1);//把经过当前点的最长路从线段树中挖去
change(1,0,n,g[u],-1);//把从当前点出发的最长路挖去
if(maxn[1]<ans) ans=maxn[1],ansx=u;//记录当前最长路
for(j=p[u];j!=-1;j=nxt[j]) change(1,0,n,g[a[j]]+1+f[u],1);
change(1,0,n,f[u],1);// 还原
}
printf("%d %d\n",ansx,ans);
return 0;
}
既然无能更改,又何必枉自寻烦忧