您的位置:首页 > 其它

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

2017-08-09 09:45 274 查看

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);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: