【JZOJ3773】【NOI2015模拟8.15】小 P 的烦恼

17 篇文章 0 订阅

Description

小 P 最近遇上了大麻烦,他的高等代数挂科了。于是他只好找高代老师求情。善良的高代老师答应不挂他,但是要求小 P 帮助他一起解决一个难题。
问题是这样的,高代老师近期要组织班上同学一起去漂流,漂流可以看做是在一张 n 个点 m 条边的有向无环图上进行的,点编号从 0 到 n-1 ,表示景点; 边是连接各景点的一定长度的河道。同时,定义编号为 s 是起点而 t 是终点。我们不妨把从 s 点到 t 点不论走什么样的路径都需要经过的边称为桥, 这些桥由于地势险要所以是危险的。现在高代老师有两条长度为 l 的安全绳,他希望用这两条安全绳覆盖尽可能长的桥,使得他们通过的桥的长度之和尽量短。

Data Constraint

对于 10%的数据,n<=10,m<=20
对于 30%的数据,n<=1000,m<= 10000
对于 100%的数据,n<=100000, m<=200000,0<=s,t,si,ti

Solution

这道题的关键是求桥。由于图是一个DAG,我们可以拓扑一遍原图,将s到每个点的方案记录下来,然后倒着拓扑一遍,记录t到每个点的方案,若一条有向边u-v满足起点到u的方案*v到终点的方案=s到t的方案,则u-v必为桥边,证明显然(因为这说明不存在其他不经过u-v而从s-t的路径)
然后在跑拓扑的时候我们顺便跑出个最短路,桥边必然在最短路上,然后问题就变成在一条路径上覆盖两条长度为len的绳子,贪心放一下O(N)解决。

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const ll maxn=2e5+5,mo=1e9+7;
ll fa[maxn],fa1[maxn],d[maxn],bz[maxn],first[maxn],last[maxn],next[maxn],value[maxn],v[maxn*20];
ll p[maxn],q[maxn],f[maxn],g[maxn],first1[maxn],last1[maxn],next1[maxn];
ll n,m,i,t,j,k,l,x,y,z,test,s,st,len,num,tot,len1,sum,mx,ans;
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){
    last1[num]=y;next1[num]=first1[x];first1[x]=num;
}
void spfa(){
    int i=0,j=1;v[1]=s;memset(d,127,sizeof(d));memset(bz,0,sizeof(bz));
    d[s]=0;bz[s]=1;
    while (i<j){
        x=v[++i];
        for (t=first[x];t;t=next[t]){
            if (d[x]+value[t]>=d[last[t]]) continue;
            d[last[t]]=d[x]+value[t];fa[last[t]]=x;fa1[last[t]]=t;
            if (!bz[last[t]])v[++j]=last[t],bz[v[j]]=1;
        }
        bz[x]=0;
    }x=st;d[0]=0;
    if (d[st]>1e9){
        d[0]=-1;return;
    }
    while (x!=s) d[++d[0]]=fa1[x],x=fa[x]; 
}
void make(){
    v[0]=0;j=0;
    for (i=0;i<n;i++)
        if (!p[i]) v[++j]=i;
    f[s]=1;i=0;
    while (i<j){
        x=v[++i];
        for (t=first[x];t;t=next[t]){
            f[last[t]]=(f[last[t]]+f[x])%mo;
            p[last[t]]--;
            if (!p[last[t]]) v[++j]=last[t];    
        }
    }
}
void make1(){
    v[0]=0;j=0;
    for (i=0;i<n;i++)
        if (!q[i]) v[++j]=i;
    g[st]=1;i=0;
    while (i<j){
        x=v[++i];
        for (t=first1[x];t;t=next1[t]){
            g[last1[t]]=(g[last1[t]]+g[x])%mo;
            q[last1[t]]--;
            if (!q[last1[t]]) v[++j]=last1[t];  
        }
    }
}
int dg(int sum,int j,int tot,int len1,int x){
    while(sum>x){
        if (sum-tot>x) sum-=tot,len1-=tot*bz[d[j]],j--,tot=value[d[j]];
        else t=x-(sum-tot),sum-=tot-t,len1-=(tot-t)*bz[d[j]],tot=t;
    }
    return len1;
}
int main(){
//  freopen("data.in","r",stdin);//freopen("data.out","w",stdout);
    scanf("%d",&test);
    while (test){test--;
        memset(first,0,sizeof(first));memset(first1,0,sizeof(first1));num=0;ans=0;
        memset(p,0,sizeof(p));memset(q,0,sizeof(q));memset(f,0,sizeof(f));memset(g,0,sizeof(g));
        scanf("%d%d%d%d%d",&n,&m,&s,&st,&len);
        for (i=1;i<=m;i++)
            scanf("%d%d%d",&x,&y,&z),lian(x,y,z),lian1(y,x),p[y]++,q[x]++;
        spfa();
        if (d[0]==-1){
            printf("-1\n");
            continue;
        }
        make();
        make1();
        for (i=0;i<n;i++)
            for (t=first[i];t;t=next[t])
                if (f[i]*g[last[t]]%mo==f[st]) bz[t]=1;
        len1=0;sum=0;j=1;tot=value[d[1]];f[0]=0;g[0]=0;
        for (i=1;i<=d[0];i++){
            if (sum+value[d[i]]<=len) sum+=value[d[i]],g[i]=len1,p[i]=value[d[i]],len1+=p[i]*bz[d[i]];
            else{
                if (value[d[i]]<=len){          
                    g[i]=len1;p[i]=len-sum;
                    while(sum+value[d[i]]>len){
                        if (sum+value[d[i]]-tot>len) sum-=tot,len1-=tot*bz[d[j]],j++,tot=value[d[j]];
                        else t=len-(sum+value[d[i]]-tot),sum-=tot-t,len1-=(tot-t)*bz[d[j]],tot=t;
                    }
                    sum+=value[d[i]];len1+=value[d[i]]*bz[d[i]];
                }else sum=len,len1=len*bz[d[i]],tot=len,j=i,g[i]=0,p[i]=len;
            }
            f[i]=max(f[i-1],len1);
            while (!bz[d[j]] && j<=i) sum-=tot,j++,tot=value[d[j]];
        }
        len1=0;sum=0;j=d[0];tot=value[d[d[0]]];mx=0;
        for (i=d[0];i>=1;i--){
            if (sum+value[d[i]]<=len) sum+=value[d[i]],len1+=value[d[i]]*bz[d[i]],t=len1;
            else{
                if (bz[d[i]]&&p[i]){
                    if (p[i]+len-sum>=value[d[i]])ans=max(ans,len1+g[i]+value[d[i]]);
                    else{
                        if (len>=value[d[i]]-p[i]){
                            t=dg(sum,j,tot,len1,len-value[d[i]]+p[i]);
                            ans=max(ans,t+g[i]+value[d[i]]);
                        }else ans=max(ans,len1+g[i]+p[i]+len-sum);
                    }
                }
                if (value[d[i]]<=len){
                    while(sum+value[d[i]]>len){
                        if (sum+value[d[i]]-tot>len) sum-=tot,len1-=tot*bz[d[j]],j--,tot=value[d[j]];
                        else t=len-(sum+value[d[i]]-tot),sum-=tot-t,len1-=(tot-t)*bz[d[j]],tot=t;
                    }
                    sum+=value[d[i]];len1+=value[d[i]]*bz[d[i]];
                }else sum=len,len1=len*bz[d[i]],j=i,tot=len;
            }
            mx=max(mx,len1);
            ans=max(ans,f[i-1]+mx);
            while (!bz[d[j]] && j>=i) sum-=tot,j--,tot=value[d[j]];
        }t=0;
        for (i=1;i<=d[0];i++)
            t+=bz[d[i]]*value[d[i]];
        ans=t-ans;
        printf("%d\n",ans);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值