【NOIP2013模拟】DY引擎

Description

给出一张无向图,问从1到n的最短路。
你可以瞬移k次,每次从u瞬移到v的条件是:u到v中存在一条不经过收费站的距离<=L的路径。当然,u或v可以是收费站。
收费站不会直接告诉你。而是给出p个提示,每个提示[x,y,z]表示编号在[x,y]这个区间中的点至少有z个收费站。
总共有m个收费站,保证唯一解。
m

Solution

首先解决收费站的问题。
把所有提示查分,那么就变成了pre[y]-pre[x-1]>=z。
还有其他一堆不等式。
那么便可以使用差分约束系统了。
接下来就直接预处理出所有点可以瞬移到的点,然后跑一边玄学算法sp(b)fa就好了。
因为有次数限制,就和GDOI2016Day2T1一样,用sp(b)fa来跑dp。
本蒟蒻这道题写了三个sp(b)faO(∩_∩)O~

Code

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define rep(i,a) for(int i=last[a];i;i=next[i])
#define N 305
#define M 100005
using namespace std;
int n,m,e,p,Len,K,l,x,y,z,ans,d[M],g[M],dis[N],f[N][55];
int last[N],t[M],c[M],next[M],go[N][N],u[M],v[M],w[M];
bool bz[N],a[N],pd[N][55];
int get() {
    char ch;while (!isdigit(ch=getchar()));
    int o=ch-48;while (isdigit(ch=getchar())) o=o*10+ch-48;
    return o;
}
void add(int x,int y,int z) {
    t[++l]=y;c[l]=z;next[l]=last[x];last[x]=l;
}
int main() {
    n=get();m=get();e=get();p=get();Len=get();K=get();
    fo(i,1,e) u[i]=get(),v[i]=get(),w[i]=get();
    fo(i,1,p) x=get(),y=get(),z=get(),add(x-1,y,z);
    fo(i,0,n-1) add(i,i+1,0),add(i+1,i,-1);
    add(0,n,m);add(n,0,-m);
    memset(dis,128,sizeof(dis));dis[0]=0;
    memset(bz,0,sizeof(bz));bz[0]=1;
    int i=0,j=1;d[1]=0;
    while (i<j) {
        rep(k,d[++i]) if (dis[t[k]]<dis[d[i]]+c[k]) {
            dis[t[k]]=dis[d[i]]+c[k];
            if (!bz[t[k]]) bz[t[k]]=1,d[++j]=t[k];
        }
        bz[d[i]]=0;
    }
    fo(i,1,n) a[i]=dis[i]-dis[i-1];
    memset(last,0,sizeof(last));l=0;
    fo(i,1,e) add(u[i],v[i],w[i]),add(v[i],u[i],w[i]);
    memset(bz,0,sizeof(bz));
    fo(st,1,n) {
        memset(dis,127,sizeof(dis));dis[st]=0;
        int i=0,j=1;d[1]=st;bz[st]=1;
        while (i<j) {
            rep(k,d[++i]) if (dis[t[k]]>dis[d[i]]+c[k]) {
                dis[t[k]]=dis[d[i]]+c[k];
                if (!a[t[k]]&&!bz[t[k]]) bz[t[k]]=1,d[++j]=t[k]; 
            }
            bz[d[i]]=0;
        }
        fo(i,1,n) if (dis[i]<=Len&&i!=st) go[st][++go[st][0]]=i;
    }
    memset(f,127,sizeof(f));f[1][0]=0;
    memset(pd,0,sizeof(pd));pd[1][0]=1;
    i=0,j=1;d[1]=1;
    while (i<j) {
        int l=g[++i];
        rep(k,d[i]) {
            if (f[t[k]][l]>f[d[i]][l]+c[k]) {
                f[t[k]][l]=f[d[i]][l]+c[k];
                if (!pd[t[k]][l]) pd[t[k]][l]=1,d[++j]=t[k],g[j]=l;
            }
        }
        if (l==K) {pd[d[i]][g[i]]=0;continue;}
        fo(q,1,go[d[i]][0]) {
            int x=go[d[i]][q];
            if (f[x][l+1]>f[d[i]][l]) {
                f[x][l+1]=f[d[i]][l];
                if (!pd[x][l+1]) pd[x][l+1]=1,d[++j]=x,g[j]=l+1;
            }
        }
        pd[d[i]][g[i]]=0;
    }ans=0x7fffffff;
    fo(i,0,K) ans=min(ans,f[n][i]);
    printf("%d",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值