JZOJ4878. 【NOIP2016提高A组集训第10场11.8】时空传送

55 篇文章 0 订阅
5 篇文章 0 订阅

Description

经过旷久的战争,ZMiG和707逐渐培养出了深厚的感♂情。他们逃到了另一块大陆上,决定远离世间的纷争,幸福地生活在一起。钟情707的neither_nor决心要把他们拆散,他要动用手中最大杀器之一——超时空传送仪来分开ZMiG和707。ZMiG和707所在的大陆可以被描述成N个点M条边的有向无环图。707和ZMiG自带魔法防御,neither_nor只可以用超时空传送仪把707传送到一个点,再把ZMiG传送到一个能到达707所在点的点,然后为ZMiG指定一条寻找707的路径,他当然想把707和ZMiG分隔的越远越好,所以他会规划一条距离最长的路线。707自然也知道这一点,但是她没有办法阻止,她唯一能做且必须做的就是利用手中的炸药炸掉大陆上的一个点,然后与这条大陆相连的边也会自动被抹去,这样就会打乱neither_nor本来的计划。但是由于707过于敏捷,他的行动必须在neither_nor之前。换句话说,707需要先选择他炸掉哪个点,之后neither_nor一定会选择一条图中存在的最长路径来分开ZMiG和707。
707想让这条路径最短,所以她要问问你他应该炸掉哪个点。

Data Constraint

对于20%的数据,N<=20,M<=40
对于40%的数据,N<=1000,M<=2000
对于100%的数据,N<=75000,M<=100000

Solution

我们设f[i]表示从任意一点到达i的最大距离,设g[i]表示从i到达其他点最大距离。打么对于一条有向边(x,y),一定满足经过这条边的最大路径为 f[x]+1+g[y] 。现在我们求出每个点的f和g后,先将所有点的g值放入队列中。然后我们按拓扑序来枚举删除哪一个点,对于当前删的点x,因为连向x的所有边都没用了,所以我们将连向x的所有边(x’,x)的 f[x]+1+g[x] 和g[x]从队列中删去,在堆中查询一个最大值更新答案,因为x连向的所有边会对下一步有贡献,所以我们将x连向的所有边(x,x’)的 f[x]+1+g[x] 和f[x]加入堆中,继续下一个点操作。

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<set>
#define ll long long
using namespace std;
const int maxn=250005;
int first[maxn],last[maxn],next[maxn],d[maxn],value[maxn],f[maxn],g[maxn];
int n,m,i,t,j,k,l,x,y,num,ans,mx,p;
int v[maxn],a[maxn],first1[maxn],last1[maxn],value1[maxn],next1[maxn];
bool bz[maxn];
multiset<int>h;
void lian(int x,int y,int z){
    last[++num]=y;next[num]=first[x];first[x]=num;value[num]=z;
}
void lian1(int x,int y,int z){
    last1[num]=y;next1[num]=first1[x];first1[x]=num;value1[num]=z;
}
void dg(){
    i=0;j=0;
    for (k=1;k<=n;k++)
        if (!a[k]) v[++j]=k;
    while (i<j){
        x=v[++i];
        for (t=first[x];t;t=next[t]){
            a[last[t]]--;
            if (!a[last[t]]) v[++j]=last[t];
        }
    }
}
int main(){
//  freopen("data.in","r",stdin);
    freopen("chronosphere.in","r",stdin);freopen("chronosphere.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (i=1;i<=m;i++)
        scanf("%d%d",&x,&y),a[y]++,lian(x,y,1),lian1(y,x,1);
    dg();
    for (l=1;l<=n;l++){
        x=v[l];
        for (t=first[x];t;t=next[t])
            f[last[t]]=max(f[last[t]],f[x]+1);
    }
    for (l=n;l>=1;l--){
        x=v[l];
        for (t=first1[x];t;t=next1[t])
            g[last1[t]]=max(g[last1[t]],g[x]+1);
    }
    for (i=1;i<=n;i++)
        h.insert(g[i]);
    ans=1e9;
    for (l=1;l<=n;l++){
        x=v[l];
        for(t=first1[x];t;t=next1[t]){
            if (!last1[t] || last1[t]>n) continue;
            h.erase(h.find(f[last1[t]]+g[x]+1));
        }
        h.erase(h.find(g[x]));
        t=*--h.end();
        if (t<ans)ans=t,p=x;
        else if (t==ans && p>x) p=x;
        for(t=first[x];t;t=next[t]){
            if (!last[t] || last[t]>n) continue;
            h.insert(f[x]+1+g[last[t]]);
        } 
        h.insert(f[x]);
    }
    printf("%d %d\n",p,ans);
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值