【BZOJ-3832】Rally 拓扑序 + 线段树 (神思路题!)

3832: [Poi2014]Rally

Time Limit: 20 Sec  Memory Limit: 128 MBSec  Special Judge
Submit: 168  Solved: 84
[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

Sample Output

1 2

HINT

Source

鸣谢Claris提供SPJ及译文

Solution

神思路!

直接求最长路径的方法是拓扑排序后DP

那么这道题先建立源汇,那么最长路径就是S-->T的最长路

实际上对于一条边<u,v>经过这条边的最长路就是S-->u的最长+<u,v>+v-->T的最长

所以定义f[x][0]和f[x][1]表示S到x的最长,x到T的最长,那么我们对一条边<u,v>他的权值定义为f[x][0]+f[v][1]

那么这个图的最长路径就转化的所有边的边权的最大值

现在就用一个数据结构去维护这些信息,支持删除,添加,最大

显然可以用堆,也可以用线段树

假设开始所有点都在T集中

按照拓扑序删点,并把该点加入S集中

把这个点有关的入边删掉,此时的最大值就是删当前点的答案,再把出边加入即可

Code

#include<iostream> 
#include<cstring> 
#include<algorithm> 
#include<cmath> 
#include<cstdio> 
#include<stack> 
using namespace std; 
void Freopen() {freopen("flower.in","r",stdin); freopen("flower.out","w",stdout);} 
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; 
}
#define MAXM 1000010 
#define MAXN 500010 
int N,M; 
struct EdgeNode{int to,next;}edge[MAXM],road[MAXM]; 
int head[MAXN],cnt,last[MAXN],tot; 
void AddEdge(int u,int v) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v;} 
void AddRoad(int u,int v) {tot++; road[tot].next=last[u]; last[u]=tot; road[tot].to=v;} 
int topo[MAXN],tp,ind[MAXN],visit[MAXN],f[MAXN][2]; 
void TopoSort() 
{ 
    int S=1,T=N; 
    stack<int>st; 
    for (int i=S; i<=T; i++) if (!ind[i]) st.push(i),topo[++tp]=i; 
    while (!st.empty()) 
        { 
            int now=st.top(); st.pop(); visit[now]=0; 
            for (int i=head[now]; i; i=edge[i].next) 
                { 
                    ind[edge[i].to]--; 
                    if (!ind[edge[i].to]) st.push(edge[i].to),topo[++tp]=edge[i].to; 
                } 
        } 
//  printf("tp=%d\n",tp); 
//  for (int i=1; i<=tp; i++) printf("%d\n",topo[i]); 
} 
void GetLongestRoad() 
{ 
    for (int i=1; i<=N; i++) 
        { 
            int now=topo[i]; 
            f[now][0]=max(f[now][0],1); 
            for (int j=head[now]; j; j=edge[j].next) 
                f[edge[j].to][0]=max(f[edge[j].to][0],f[now][0]+1); 
        } 
    for (int i=N; i>=1; i--) 
        { 
            int now=topo[i]; 
            f[now][1]=max(f[now][1],1); 
            for (int j=head[now]; j; j=edge[j].next) 
                f[now][1]=max(f[edge[j].to][1]+1,f[now][1]); 
        } 
} 
struct SegmentTreeNode{int l,r,maxx,num;}tree[MAXN<<2]; 
inline void Update(int now) {tree[now].maxx=max(tree[now<<1].maxx,tree[now<<1|1].maxx);} 
void BuildTree(int now,int l,int r) 
{ 
    tree[now].l=l,tree[now].r=r; 
    if (l==r) return;
    int mid=(l+r)>>1; 
    BuildTree(now<<1,l,mid); 
    BuildTree(now<<1|1,mid+1,r); 
    Update(now);
} 
void Change(int now,int loc,int D) 
{ 
    int l=tree[now].l,r=tree[now].r; 
    if (l==r)  
        {tree[now].num+=D; tree[now].maxx=tree[now].num>0? l:-1; tree[now].num=max(tree[now].num,0); return;} 
    int mid=(l+r)>>1; 
    if (loc<=mid) Change(now<<1,loc,D); else Change(now<<1|1,loc,D); 
    Update(now); 
} 
int Query(int now,int L,int R) 
{ 
    int l=tree[now].l,r=tree[now].r; 
    if (L<=l && R>=r) return tree[now].maxx; 
    int mid=(l+r)>>1,re=-0x7fffffff; 
    if (L<=mid) re=max(re,Query(now<<1,L,R)); 
    if (R>mid) re=max(re,Query(now<<1|1,L,R)); 
    return re; 
} 
int MaxLen,Pos; 
int main() 
{ 
//  Freopen(); 
    N=read(),M=read(); 
    for (int x,y,i=1; i<=M; i++) 
        x=read(),y=read(),AddEdge(x,y),AddRoad(y,x),ind[y]++; 
    TopoSort(); 
    GetLongestRoad(); 
    BuildTree(1,0,N); 
    MaxLen=0x7fffffff; 
    for (int i=1; i<=N; i++) Change(1,f[i][1],1); 
    for (int i=1; i<=N; i++) 
        { 
            int now=topo[i]; 
            Change(1,f[now][1],-1); 
            for (int j=last[now]; j; j=road[j].next) 
                Change(1,f[road[j].to][0]+f[now][1],-1); 
            if (Query(1,0,N)<MaxLen) MaxLen=Query(1,0,N),Pos=now; 
            Change(1,f[now][0],1); 
            for (int j=head[now]; j; j=edge[j].next) 
                Change(1,f[now][0]+f[edge[j].to][1],1); 
        } 
    printf("%d %d\n",Pos,MaxLen-1); 
    return 0; 
}

 

posted @ 2016-08-03 16:59  DaD3zZ  阅读(404)  评论(0编辑  收藏  举报