【bzoj3832】【poi2014】【Rally】【拓扑排序+线段树】

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

Sample Output

1 2
题解:
非常厉害的思路.
设f[i],g[i]分别表示在到点i的最长路径和从点i开始的最长路径.
这个显然可以用拓扑排序处理. 
对于一条边(u,v),它能产生的最长路径是f[u]+e[i].v+g[u]
我们用线段树维护当前所有合法边产生的最长路径的最大值.  
首先把g[i]数组加入线段树.
按拓扑序依次枚举每一个点.
把指向它的边产生的最长路径和从这个点开始的最长路径从线段树中删除.
当前全局最大值就是删掉这个点的最长路径.
然后把这个点连出去的边产生的最长路径加入线段树
也要把到这个点的最长路径加入线段树.
代码:
#include<iostream>
#include<cstdio>
#define N 500010
#define M 1000010
using namespace std;
int point[N],head[N],next[M<<2],d1[N],d2[N],q[N],cnt,n,m,x,y;
int t[N<<2],f[N],g[N],ans(99999999),p,c[N];
struct use{int st,en,v;}e[M<<2];
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline void add(int x,int y,int v){
  d1[y]++;d2[x]++;
  next[++cnt]=point[x];point[x]=cnt;e[cnt].en=y;e[cnt].v=v;
  next[++cnt]=head[y];head[y]=cnt;e[cnt].en=x;e[cnt].v=v;
}
void insert(int k,int l,int r,int p,int v){
   int mid=(l+r)>>1;
   if (l==r){
     c[l]+=v;
     if (c[l]>0) t[k]=l;
     else{c[l]=0;t[k]=-1;}
     return;
   }
   if (p<=mid) insert(k<<1,l,mid,p,v);
   else insert(k<<1|1,mid+1,r,p,v);
   t[k]=max(t[k<<1],t[k<<1|1]); 
}
inline void pre(){
   int h(0),t(0);
   for (int i=1;i<=n;i++) if (!d2[i]) q[++t]=i;
   while (h<t){
     int u=q[++h];
     for (int i=head[u];i;i=next[i]){
        if (g[e[i].en]<g[u]+e[i].v) g[e[i].en]=g[u]+e[i].v;
        if (!(--d2[e[i].en])) q[++t]=e[i].en;
     } 
   }
   h=0;t=0;
   for (int i=1;i<=n;i++) if (!d1[i]) q[++t]=i;
   while (h<t){
     int u=q[++h];
     for (int i=point[u];i;i=next[i]){
       if (f[e[i].en]<f[u]+e[i].v) f[e[i].en]=f[u]+e[i].v;
       if (!(--d1[e[i].en])) q[++t]=e[i].en;
     }
   }	
}
int main(){
  n=read();m=read();
  for (int i=1;i<=m;i++){x=read();y=read();add(x,y,1);}
  pre();
  for (int i=1;i<=n;i++) insert(1,0,n,g[i],1);
  for (int k=1;k<=n;k++){
    int u=q[k];
    for (int i=head[u];i;i=next[i])
      insert(1,0,n,f[e[i].en]+e[i].v+g[u],-1);
    insert(1,0,n,g[u],-1);
    if (t[1]<ans&&k!=1) ans=t[1],p=u;
    for (int i=point[u];i;i=next[i])
	  insert(1,0,n,f[u]+e[i].v+g[e[i].en],1);
	insert(1,0,n,f[u],1);
  }
  cout<<p<<' '<<ans<<endl;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值