【JZOJ 3773】 小 P 的烦恼

58 篇文章 0 订阅
28 篇文章 0 订阅

Description

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

Analysis

比赛想到正解没时间写系列
结果我dfs的时候在9万9千多爆栈,TM的n最大10w,各种脏话上脑。。。。。
可以将所有的桥弄出来,比较简单的算法可以用拓扑序弄出走到某个点的方案数,也就弄出了走某条边的方案数
如果走某条边的方案数=从st到en的方案数,那么边为桥

然后是一个线段覆盖(桥是线段,需要被绳子覆盖)模型,考虑绳子可能放在哪些位置

显然只可能放在线段两个端点
如果从左到右枚举,右边绳子放的位置(必定是某条线段右端点,且方向为左)呢
另一条绳子的取值可以用指针移动来搞,甚至可以弄到线性

整题线性解决

Code

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,b,a) for(int i=b;i>=a;i--)
#define efo(i,v) for(int i=last[v];i;i=next[i])
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define mset(a,x) memset(a,x,sizeof(a))
using namespace std;
typedef long long ll;
typedef double db;
void read(int &n)
{
    n=0;char ch;int p=1;
    for(ch=getchar();ch<'0' || '9'<ch;ch=getchar())
        if(ch=='-') p=-1;
    for(;'0'<=ch && ch<='9';ch=getchar()) n=n*10+ch-'0';
    n*=p;
}
const int N=1e5+5,M=2e5+5,mo=1e9+7;
const ll INF=1e17;
int n,m,num,st,en,L,tot,to[M],next[M],wei[M],last[N],in[N],out[N];
int fto[M],fnext[M],flast[N],sm[N];
int ans,qi[N],pmx[N];
ll dis[N],f[N],g[N];
struct node
{
    int x,y;
}a[N];
bool cmp(node a,node b)
{
    return a.x<b.x;
}
bool bz[N];
struct edge
{
    int u,v,w;
}b[M];
queue<int> q;
void link(int u,int v,int w)
{
    to[++tot]=v,next[tot]=last[u],wei[tot]=w,last[u]=tot;
    fto[tot]=u,fnext[tot]=flast[v],flast[v]=tot;
}
void pre()
{
        fo(i,0,n) dis[i]=INF;
        mset(f,0);mset(g,0);
        dis[st]=0,f[st]=1;
        for(q.push(st);!q.empty();q.pop())
        {
            int u=q.front();
            efo(i,u)
            {
                int v=to[i];
                dis[v]=min(dis[v],dis[u]+wei[i]);
                f[v]=(f[v]+f[u])%mo;
                if(!--in[v]) q.push(v);
            }
        }
        mset(bz,0);
        g[en]=1;
        for(q.push(en);!q.empty();q.pop())
        {
            int u=q.front();
            for(int i=flast[u];i;i=fnext[i])
            {
                int v=fto[i];
                g[v]=(g[v]+g[u])%mo;
                if(!--out[v]) q.push(v);
            }
        }
        qi[0]=0;
        fo(i,1,m)
        {
            int u=b[i].u,v=b[i].v;
            if(f[en]==f[u]*g[v]%mo) qi[++qi[0]]=i;
        }
}
void work()
{
    num=0;
    mset(a,0);
    fo(j,1,qi[0])
    {
        int i=qi[j];
        a[++num].x=dis[b[i].u],a[num].y=dis[b[i].v];
        if(a[num].x>a[num].y) swap(a[num].x,a[num].y);
    }
    sort(a+1,a+num+1,cmp);
    mset(sm,0);
    fo(i,1,num) sm[i]=sm[i-1]+a[i].y-a[i].x;
    mset(pmx,0);
    ans=0;
    int l=1,k=0,j=0;
    fo(i,1,num)
    {
        while(a[i].y-a[l].x>L && l<=num) l++;
        int t=sm[i]-sm[l-1]+min(a[l-1].y-a[l-1].x,max(0,L-(a[i].y-a[l-1].y)));
        pmx[i]=max(pmx[i-1],t);
        int pos=a[i].y-L,rs=0;
        while(a[k+1].x<=pos && k<num) k++;
        if(k)
        {
            if(pos-a[k].x>=L) rs=L;
            else
            {
                if(a[k].y<=pos) rs=max(rs,pmx[k]);
                else
                {
                    while(pos-a[j].x>L && j<=num) j++;
                    rs=pos-a[k].x+sm[k-1]-sm[j-1];
                    rs+=min(a[j-1].y-a[j-1].x,max(0,L-(pos-a[j-1].y)));
                    rs=max(rs,pmx[k-1]);
                }
            }
        }
        ans=max(ans,t+rs);
    }
}
int main()
{
    int u,v,w,T;
    read(T);
    while(T--)
    {
        tot=0;mset(last,0);mset(flast,0);mset(in,0);mset(out,0);
        read(n),read(m),read(st),read(en),read(L);
        fo(i,1,m)
        {
            read(u),read(v),read(w);
            b[i].u=u,b[i].v=v,b[i].w=w;in[v]++,out[u]++;
            link(u,v,w);
        }
        pre();
        if(dis[en]==INF){printf("-1\n");continue;}
        work();
        printf("%d\n",sm[num]-ans);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值