bzoj 3832: [Poi2014]Rally

首先建图,S向每个点连边,每个点向T连边,那么问题就变成了求S-T最长路-2
拓扑图的每条边都是从拓扑序小的点连到拓扑序大的点
经过某条边的最长路是从S沿最长路走到这条边的x,再从y沿最长路走到T
所以,扫描整个序列,维护所有pos[e[i].x]<i 且 i<pos[e[i].t]的经过这条边的最长路,然后取最大值。
维护所有的边,就是在扫描到x的时候,把所有到达x的边删除,查询,把所有从x出发的边加入。
我一直在想把最长链拉出来,然后用类似上面的方法维护,但没有想到拓扑图本身就可以成为一个序列。
    
    
    
    
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#define ll long long
#define inf 1e9
#define eps 1e-8
#define md
#define N 500010
using namespace std;
struct yts { int x,t,ne;} e[2000010];
int q[N],du[N],v[N],f[N],g[N];
vector<int> vec1[N],vec2[N];
multiset<int> st;
int S,T,num;
void put(int x,int y)
{
num++; e[num].x=x; e[num].t=y; du[y]++;
e[num].ne=v[x]; v[x]=num;
}
 
void tp()
{
int h=0,w=0;
for (int i=1;i<=T;i++) if (du[i]==0) q[++w]=i;
while (h!=w)
{
int x=q[++h];
for (int i=v[x];i;i=e[i].ne)
{
int y=e[i].t;
du[y]--; if (du[y]==0) q[++w]=y;
}
}
for (int j=1;j<=T;j++)
{
int x=q[j];
for (int i=v[x];i;i=e[i].ne) f[e[i].t]=max(f[e[i].t],f[x]+1);
}
for (int j=T;j;j--)
{
int x=q[j];
for (int i=v[x];i;i=e[i].ne) g[x]=max(g[x],g[e[i].t]+1);
}
}
 
void insert(int x)
{
for (int i=0;i<vec1[x].size();i++) st.insert(f[x]+g[vec1[x][i]]+1);
}
 
void del(int x)
{
for (int i=0;i<vec2[x].size();i++) st.erase(st.find(g[x]+f[vec2[x][i]]+1));
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
put(x,y);
vec1[x].push_back(y); vec2[y].push_back(x);
}
S=n+1; T=n+2;
for (int i=1;i<=n;i++)
{
put(S,i); put(i,T); vec1[i].push_back(T); vec2[i].push_back(S);
}
tp();
for (int i=1;i<=n;i++) st.insert(g[i]+1);
int mn=inf,ans=0;
for (int i=2;i<T;i++)
{
int x=q[i];
del(x);
int tmp=*--st.end();
if (tmp<mn) { mn=tmp; ans=x;}
insert(x);
}
printf("%d %d\n",ans,mn-2);
return 0;
}

你可能感兴趣的:(bzoj 3832: [Poi2014]Rally)