BZOJ 4400(tjoi2012 桥-最短路树)

给一幅n点m边的无向图,删一条边使从点1到点n的最短路最大,求最短路最大值,以及这种情况下可以删的边的数量。
数据保证玩家可以从岛屿1到达岛屿n。
1<=n<=100000,1<=m<=200000

建最短路树,显然必须删除最短路树上1->n路径上的一条边

考虑删了一条边后,至少要走一条不在最短路树上的边,假设它是a->b
那么最短路至少为S->a->b->t

其中S->a为起点出发的最短路树上的路径,b->t则为终点出发的。
这种情况下可能删的边满足
- 在最短路树上1->n路径上
- 不在S->a->b->t路径上

暴力枚举a->b求解

#include<bits/stdc++.h>
using namespace std;
#define For(i,n) for(int i=1;i<=n;i++)
#define Fork(i,k,n) for(int i=k;i<=n;i++)
#define Rep(i,n) for(int i=0;i<n;i++)
#define ForD(i,n) for(int i=n;i;i--)
#define ForkD(i,k,n) for(int i=n;i>=k;i--)
#define RepD(i,n) for(int i=n;i>=0;i--)
#define Forp(x) for(int p=Pre[x];p;p=Next[p])
#define Forpiter(x) for(int &p=iter[x];p;p=Next[p])  
#define Lson (o<<1)
#define Rson ((o<<1)+1)
#define MEM(a) memset(a,0,sizeof(a));
#define MEMI(a) memset(a,127,sizeof(a));
#define MEMi(a) memset(a,128,sizeof(a));
#define INF (2139062143)
#define F (100000007)
#define pb push_back
#define mp make_pair 
#define fi first
#define se second
#define vi vector<int> 
#define pi pair<int,int>
#define SI(a) ((a).size())
typedef long long ll;
typedef unsigned long long ull;
ll mul(ll a,ll b){return (a*b)%F;}
ll add(ll a,ll b){return (a+b)%F;}
ll sub(ll a,ll b){return (a-b+llabs(a-b)/F*F+F)%F;}
void upd(ll &a,ll b){a=(a%F+b%F)%F;}
int read()
{
    int x=0,f=1; char ch=getchar();
    while(!isdigit(ch)) {if (ch=='-') f=-1; ch=getchar();}
    while(isdigit(ch)) { x=x*10+ch-'0'; ch=getchar();}
    return x*f;
} 

#define MAXN (100000+10)
#define MAXM (400000+10)
int n,m;
int u[MAXM],v[MAXM],c[MAXM];
int Edge[MAXM],Next[MAXM],Pre[MAXM],Weight[MAXM],Siz=1;
void addedge(int u,int v,int c){
    Edge[++Siz]=v;
    Weight[Siz]=c;
    Next[Siz]=Pre[u];
    Pre[u]=Siz;
}
void addedge2(int u,int v,int c){addedge(u,v,c),addedge(v,u,c);}

bool vis[MAXN];
int d[MAXN][2];
int dep[MAXN]={0},path[MAXN]={0};
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q;; 
void spfa(int s,int op){
    d[s][op]=0;
    MEM(vis)
    q.push(mp(0,s)); vis[s]=1; dep[s]=1;
    while(!q.empty()) {
        int now = q.top().se, d2 = q.top().fi;
        q.pop();
        if (d2!=d[now][op]) continue;

        vis[now] = 0;
        Forp(now) {
            int v = Edge[p];
            if (d[v][op]>d[now][op]+Weight[p]) {
                d[v][op]=d[now][op]+Weight[p];
                q.push(mp(d[v][op],v) );
                vis[v]=1;
                if (!op) {
                    path[v]=p;
                }
            }
        }
    }
}
int lis[MAXN],cnt=0,flag[MAXM]={0};
void pre() {
    for(int u=n;u;flag[path[u]/2]=1,u=Edge[path[u]^1]) lis[++cnt]=u;
    reverse(lis+1,lis+1+cnt);
    For(i,cnt) dep[lis[i]]=i;
    For(i,n) if (!dep[i]) {
        int u;
        for(u=i;!dep[u];u=Edge[path[u]^1]) ;
        int d=dep[u];
        u=i;
        while(!dep[u]) dep[u]=d,u=Edge[path[u]^1];
    }

} 
int Ans[MAXN];
int minv[MAXN*4];
void build() {
    MEMI(minv)

}
void modi(int L,int R,int l,int r,int o,int c) {
    if (l<=L && R<=r) {
        minv[o]=min(minv[o],c);
        return;
    }
    int m=(L+R)>>1;
    if (l<=m) modi(L,m,l,r,Lson,c);
    if (m<r) modi(m+1,R,l,r,Rson,c);
}
void query(int L,int R,int o,int ans) { 
    ans=min(ans,minv[o]);
    if (L==R) Ans[L]=ans;
    else {
        int m=(L+R)>>1;
        query(L,m,Lson,ans);
        query(m+1,R,Rson,ans);      
    } 
}


void work(int u,int v,int c){
    if (dep[u] == dep[v] ) return;
    if (dep[u] > dep[v] ) swap(u,v);
    modi(1,cnt-1,dep[u],dep[v]-1,1,d[u][0]+d[v][1]+c);
}

int main()
{
//  freopen("bzoj4400.in","r",stdin);
//  freopen(".out","w",stdout);

    n = read(); m = read(); 
    For(i,m) {
        u[i]=read(); v[i] = read(); c[i] = read();
        addedge2(u[i],v[i],c[i]);
    }
    MEMI(d)
    spfa(1,0); spfa(n,1);
    pre();
    build();
    For(i,m) if (!flag[i]) {
        work(u[i],v[i],c[i]);
    }
    query(1,cnt-1,1,INF); 
    int ans1=Ans[1],ans2=0;
    For(i,cnt-1) ans1=max(ans1,Ans[i]);
    if (ans1!=d[n][0]) For(i,cnt-1) ans2+= (bool)(ans1 == Ans[i]);
    else ans2=m;
    printf("%d %d\n",ans1,ans2);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值