JZOJ3773【NOI模拟】小 P 的烦恼(拓扑排序,贪心,找桥)

来源:互联网 发布:nginx module 编辑:程序博客网 时间:2024/05/21 16:21

Description

小 P 最近遇上了大麻烦,他的高等代数挂科了。于是他只好找高代老师求情。善良的高代老师答应不挂他,但是要求小 P 帮助他一起解决一个难题。

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

Solution

首先这题最重要的问题就是找桥边,那么我们有一个很方便的方法,因为是DAG,所以我们可以对于一条边i–j,如果S到i的方案数*j到T的方案数=S到T的方案数,那么这条边就是桥边。
求方案数,正着连边拓扑一下,反着连边拓扑一下。注意方案数模上一个大质数,如果怕错可以搞两个质数。
然后把S-T上的路径提出来,先求一个p[i]和q[i]表示从i开始向左向右长度为L能覆盖的桥的长度,然后把他们前缀后缀max一下,枚举i,p[i]和q[i+1]合并。
我们还要考虑两个桥合并的情况,那么就相当于用2*L的长度来覆盖,和上面求p、q的方法一样。

Code

#include<iostream>#include<stdio.h>#include<string.h>#include<algorithm>#include<math.h>#define fo(i,a,b) for(i=a;i<=b;i++)#define fod(i,a,b) for(i=a;i>=b;i--)#define rep(i,a) for(i=first[a];i;i=next[i])#define rep1(i,a) for(i=first1[a];i;i=next1[i])using namespace std;typedef long long ll;const int maxn=2e5+7,mo=1e9+7;ll i,j,k,l,t,n,m,ans,T,S,cas,x,y,z,L,u,r,ans1,oo;ll first[maxn],last[maxn],next[maxn],chang[maxn],num;ll first1[maxn],last1[maxn],next1[maxn],chang1[maxn],num1;ll d[maxn*50],c[maxn],cc[maxn],head,tail,g[maxn],tot,p[maxn],q[maxn];ll f[maxn],f1[maxn];bool bz[maxn],az,cz[maxn];struct node{    ll a,b,c;}e[maxn];void add(ll x,ll y,ll z){    last[++num]=y,next[num]=first[x],first[x]=num;chang[num]=z;    last1[++num1]=x,next1[num1]=first1[y],first1[y]=num1;chang1[num1]=z;}void spfa(){    ll i,j,x;    memset(bz,0,sizeof(bz));head=0;    memset(g,127/3,sizeof(g));g[d[tail=1]=S]=0;bz[S]=1;oo=g[0];    while(head<tail){        x=d[++head];        rep(i,x){            if(g[x]+chang[i]<g[last[i]]){                g[last[i]]=g[x]+chang[i];                if(!bz[last[i]])bz[last[i]]=1,d[++tail]=last[i];            }        }        bz[x]=0;    }}void dfs(int x,int y){    if(x==T){az=1;return;}    int i;    rep(i,x){        if(last[i]!=y&&g[last[i]]-chang[i]==g[x]){            dfs(last[i],x);            if(az){                e[++tot].a=g[last[i]];e[tot].b=chang[i];                e[tot].c=0;if(cz[i]){                e[tot].c=1,ans1+=chang[i];            }                return;            }        }    }}bool cmp(node x,node y){return x.a<y.a;}int main(){ //   freopen("fan.in","r",stdin);    for(scanf("%lld",&cas);cas;cas--){        scanf("%lld%lld%lld%lld%lld",&n,&m,&S,&T,&L);S++,T++;        num=num1=0;memset(first,0,sizeof(first));ans=ans1=0;        memset(first1,0,sizeof(first1));        memset(c,0,sizeof(c));memset(cc,0,sizeof(cc));        memset(f,0,sizeof(f));memset(f1,0,sizeof(f1));        fo(i,1,m){            scanf("%lld%lld%lld",&x,&y,&z);x++,y++;            add(x,y,z);            c[y]++;cc[x]++;        }        head=tail=0;d[++tail]=S;f[S]=1;        while(head<tail){            rep(i,d[++head]){                c[last[i]]--;(f[last[i]]+=f[d[head]])%=mo;                if(!c[last[i]])d[++tail]=last[i];            }        }        head=tail=0;d[++tail]=T;f1[T]=1;        while(head<tail){            rep1(i,d[++head]){                cc[last1[i]]--;(f1[last1[i]]+=f1[d[head]])%=mo;                if(!cc[last1[i]])d[++tail]=last1[i];            }        }        spfa();tot=0;        if(g[T]==oo){            printf("-1\n");            continue;        }        memset(cz,0,sizeof(cz));        fo(i,1,n)rep(j,i)if(f[i]*f1[last[j]]%mo==f[T])cz[j]=1;        az=0;dfs(S,0);        sort(e+1,e+1+tot,cmp);e[tot+1]=(node){0,0,0};        memset(p,0,sizeof(p));memset(q,0,sizeof(q));        l=1;r=0;t=0;u=0;        fo(i,1,tot){            t+=e[i].b,u+=e[i].c*e[i].b;            while(l<=i&&t>L)t-=e[l].b,u-=e[l].c*e[l].b,l++;            q[i]=u;if(e[l-1].c)q[i]+=L-t;        }        l=1;t=u=0;        fo(i,1,tot){            t+=e[i].b,u+=e[i].c*e[i].b;            while(l<=i&&t>L*2)t-=e[l].b,u-=e[l].c*e[l].b,l++;            if(e[l-1].c)ans=max(ans,u+L*2-t);else ans=max(ans,u);        }        r=tot;t=0;u=0;        fod(i,tot,1){            t+=e[i].b,u+=e[i].c*e[i].b;            while(r>=i&&t>L)t-=e[r].b,u-=e[r].c*e[r].b,r--;            p[i]=u;if(e[r+1].c)p[i]+=L-t;        }        fo(i,1,tot)q[i]=max(q[i],q[i-1]);fod(i,tot,1)p[i]=max(p[i+1],p[i]);        fo(i,1,tot)ans=max(q[i-1]+p[i],ans);        printf("%lld\n",ans1-ans);    }}
原创粉丝点击