NOIP2013模拟】DY引擎 题解+代码

17 篇文章 0 订阅
2 篇文章 0 订阅

Description

BOSS送给小唐一辆车。小唐开着这辆车从PKU出发去ZJU上课了。
众所周知,天朝公路的收费站超多的。经过观察地图,小唐发现从PKU出发到ZJU的所有路径只会有N(2<=N<=300)个不同的中转点,其中有M(max(0, N-100) <=M<=N)个点是天朝的收费站。N个中转点标号为1…N,其中1代表PKU,N代表ZJU。中转点之间总共有E(E<=50,000)条双向边连接。
每个点还有一个附加属性,用0/1标记,0代表普通中转点,1代表收费站。当然,天朝的地图上面是不会直接告诉你第i个点是普通中转点还是收费站的。地图上有P(1<=P<=3,000)个提示,用[u, v, t]表示:[u, v]区间的所有中转点中,至少有t个收费站。数据保证由所有标记得到的每个点的属性是唯一的。
车既然是BOSS送的,自然非比寻常了。车子使用了世界上最先进的DaxiaYayamao引擎,简称DY引擎。DY引擎可以让车子从U瞬间转移到V,只要U和V的距离不超过L(1<=L<=1,000,000),并且U和V之间不能有收费站(小唐良民一枚,所以要是经过收费站就会停下来交完钱再走)。
DY引擎果然是好东西,但是可惜引擎最多只能用K(0<=K<=30)次。

Input

第一行有6个整数N,M,E,P,L,K分别代表:N个中转点,M个收费站,E条边,P个提示,DY引擎的有效距离L,DY引擎的使用次数K。
接下去E行,每行有3个整数u,v,w(1<=u, v<=N; 1<=w<=1,000,000)表示:u和v之间有一条长度为w的双向边。
接下去P行,每行有3个整数u,v,t(1<=u<=v<=N; 0<=t<=u-v+1)表示: [u, v] 标号区间至少有t个收费站。

Output

输出一个整数,表示小唐从PZU开到ZJU用的最短距离(瞬间转移距离当然是按0来计算的)。

Sample Input

6 2 6 2 5 1
1 2 1
2 3 2
3 6 3
1 4 1
4 5 2
5 6 3
2 5 2
4 6 2

Sample Output

1
【样例解释】
4、5是收费站。1->2(1)->6(1)

Data Constraint

对于30%的数据保证:
2<=N<=30,max(0, N-10) <=M<=N,0<=k<=10
对于100%的数据保证:
2<=N<=300,max(0, N-100) <=M<=N,E<=50,000,1<=P<=3,000,1<=L<=1,000,000,0<=K<=30

Solution

其实呢,这道题是由两道小题拼起来的。

1.对于一些约束,判断每个点是否为关键点

设s[i]表示1~i中收费站的数量,那么第i个点的状态即为s[i]-s[i-1]
那么它给出的条件([u, v] 标号区间至少有t个收费站)就是
s[v]s[u1]>=t
隐含条件
s[i]s[i1]>=0
s[i1]s[i]>=1
那么按照查分约束那样,连边构图,从0开始求最长路即可。
详细可以点这里
单元最短路*1

2.在神奇条件的情况下求最短路

分为两步
首先,两个点如果能互相通过引擎飞过去,那么距离为0,预处理一下即可。N<=300那就Floyd做完。枚举k时注意判断k不是收费站
多元最短路*1
接着做最短路,单元最短路*2

搞定

Code

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define N 1000
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
int a[N][N],b[N][N],c[N][N],s[N],n,m,e,p,l,kk,next[500000],last[500000],to[500000],data[500000],dd[5000000],d[5000000][2],tot=0;
void putin(int x,int y,int z)
{
    next[++tot]=last[x];last[x]=tot;to[tot]=y;data[tot]=z;
}

int main()
{
    scanf("%d%d%d%d%d%d",&n,&m,&e,&p,&l,&kk);
    fo(i,1,n) fo(j,1,n) a[i][j]=1000000000;
    fo(i,1,e)
    {
        int x,y,z;scanf("%d%d%d",&x,&y,&z);
        z=min(z,a[x][y]);
        c[x][y]=c[y][x]=a[x][y]=a[y][x]=z;
    }
    fo(i,1,p)
    {
        int x,y,z;scanf("%d%d%d",&x,&y,&z);
        putin(x-1,y,z);
    }
    fo(i,1,n) putin(i-1,i,0),putin(i,i-1,-1);
    {
        int i=0,j=1,bz[N];dd[1]=0;
        memset(s,128,sizeof(s));s[0]=0;memset(bz,0,sizeof(bz));bz[0]=1;
        for(;i<j;)
        {
            int x=dd[++i];
            for(int k=last[x];k;k=next[k])
            {
                if (s[to[k]]<s[x]+data[k])
                {
                    s[to[k]]=s[x]+data[k];
                    if (bz[to[k]]==0)
                    {
                        dd[++j]=to[k];bz[to[k]]=1;
                    }
                }   
            }
            bz[x]=0;
        }
    }
    for(int i=n;i;i--) s[i]=s[i]-s[i-1];
    fo(k,1,n)
    if (s[k]!=1)
    {
        fo(i,1,n)
        if(i!=k)
            fo(j,1,n)
            if(i!=j && j!=k)
            {
                a[i][j]=min(a[i][j],a[i][k]+a[k][j]);
            //  if (a[i][j]<=l) b[i][j]=b[j][i]=1;
            }
    }
    int f[N][40],bz[N][N];int i=0,j=1;
    fo(i,1,n) if(b[1][i]==1) printf("%d\n",i);
    d[1][0]=1;d[1][1]=0;memset(f,127,sizeof(f));memset(bz,0,sizeof(bz));f[1][0]=0;bz[1][0]=1;
    for(;i<j;)
    {
        int x=d[++i][0];
        fo(y,1,n)
        {
            if (c[x][y]>0 && f[y][d[i][1]]>f[x][d[i][1]]+c[x][y])
            {
                f[y][d[i][1]]=f[x][d[i][1]]+c[x][y];
                if (bz[y][d[i][1]]==0)
                {
                    d[++j][0]=y;d[j][1]=d[i][1];bz[y][d[i][1]]=1;
                }
            }
            if (d[i][1]<kk && a[x][y]<=l && f[y][d[i][1]+1]>f[x][d[i][1]])
            {
                f[y][d[i][1]+1]=f[x][d[i][1]];
                if (bz[y][d[i][1]+1]==0)
                {
                    d[++j][0]=y;d[j][1]=d[i][1]+1;bz[y][d[j][1]]=1;
                }
            }
        }
        bz[x][d[i][1]]=0;
    }
    int ans=2147483647;
    fo(i,0,kk) ans=min(ans,f[n][i]);
    printf("%d\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值