3832: [Poi2014]Rally
Time Limit: 20 Sec Memory Limit: 128 MBSec Special JudgeSubmit: 113 Solved: 56
[ Submit][ Status][ Discuss]
Description
Input
Output
Sample Input
1 3
1 4
3 6
3 4
4 5
Sample Output
HINT
Source
题解: 线段树+拓扑排序
非常神奇的一个思路。
f[i] 表示从起始点到该点的最长路径
g[i] 表示从该点到终点(无法再走)的最长路径
对于边(u,v) ,那么他能产生的最长路就是f[u]+1+g[v]
于是我们就是可以建立一棵权值线段树,来维护删除某个节点后的最长路。
刚开始的时候先把所有点的g[i]加入线段树,然后按照拓扑序一次删除每个节点。
在删除节点的时候要把所有用他的入边更新的答案删去,再把g[i]从这个点开始的最长路删去,为什么只用删去这一条路呢?因为我们刚开始加入的时候就只加入了这个点之后的最长路。然后这时线段树中的最大值就是删去该点后的答案。那么有人会有疑问?一条最长路上的每条边能产生的最长路的值都是相同的那么只删除这个点的,会不会在这条路径上的其他边的最大值会影响答案呢?其实是不会的,因为我们是按照拓扑序来搞的,他后面的点只加入了g[x],根本没有加入这条路径的信系。
然后再把这个点的出边所能产生的最长路加入线段树,并把f[i]加入线段树,因为如果之后再用该点更新答案的话,一定会选择到该点的最长路,其他的路径就没有用了。之所以要把所有出边产生的最长路加入线段树,是因为在删除这个点之后的点的时候,可能会把这个点以后最长路破坏,所有需要把所有路径记录下来,保证可以通过所有可能的路径更新答案。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 1000003
using namespace std;
int n,m;
int next[N],point[N],v[N],f[N],ins[N],q[N],tot;
int next1[N],point1[N],v1[N],g[N],outs[N],p[N],tot1;
int cnt[N*4],maxn[N*4];
void add(int x,int y)
{
tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y;
}
void add1(int x,int y)
{
tot1++; next1[tot1]=point1[x]; point1[x]=tot1; v1[tot1]=y;
}
void solve()
{
int tail=0; int head=0;
for (int i=1;i<=n;i++)
if (!ins[i]) q[++tail]=i,f[i]=0;
while (head<tail)
{
int now=q[++head];
for (int i=point[now];i;i=next[i])
{
if (f[v[i]]<f[now]+1) f[v[i]]=f[now]+1;
if (!--ins[v[i]]) q[++tail]=v[i];
}
}
tail=0; head=0;
for (int i=1;i<=n;i++)
if (!outs[i]) p[++tail]=i,g[i]=0;
while (head<tail)
{
int now=p[++head];
for (int i=point1[now];i;i=next1[i])
{
if (g[v1[i]]<g[now]+1) g[v1[i]]=g[now]+1;
if (!--outs[v1[i]]) p[++tail]=v1[i];
}
}
}
void pointchange(int now,int l,int r,int x,int v)
{
if (l==r)
{
cnt[now]+=v;
if (cnt[now]>0) maxn[now]=l;
else maxn[now]=-1,cnt[now]=0;//这里需要注意不能无限制的减下去,因为有可能这个权值的不止一个,所以会删不止一次,但是我们在加入的时候,一定需要一下就把这个值加上去,而不是先恢复之前减掉的
return;
}
int mid=(l+r)/2;
if (x<=mid) pointchange(now<<1,l,mid,x,v);
else pointchange(now<<1|1,mid+1,r,x,v);
maxn[now]=max(maxn[now<<1],maxn[now<<1|1]);
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
int x,y; scanf("%d%d",&x,&y);
add(x,y); ins[y]++;
add1(y,x); outs[x]++;
}
solve();
int ans=1000000000; int ansx=0;
for (int i=1;i<=n;i++) pointchange(1,0,n,g[i],1);
for (int i=1;i<=n;i++)
{
int x=q[i];
for (int i=point1[x];i;i=next1[i])
pointchange(1,0,n,g[x]+1+f[v1[i]],-1);
pointchange(1,0,n,g[x],-1);
if (maxn[1]<ans) ans=maxn[1],ansx=x;
for (int i=point[x];i;i=next[i])
pointchange(1,0,n,f[x]+1+g[v[i]],1);
pointchange(1,0,n,f[x],1);
}
printf("%d %d\n",ansx,ans);
}