【JZOJ5430】【NOIP2017提高A组集训10.27】图

Description

有一个n个点的无向图,给出m条边,每条边的信息形如< x,y,c,r>给出q组询问形如< u,v,l,r>接下来解释询问以及边的意义询问表示,一开始你在点u上,然后按顺序处理编号从l到r的边对于一条边< x,y,c,r>,你可以进行两种操作:
如果你当前在x点或者y点上,那么你可以走这条边(从x到y或从y到x)并付出c的代价(当然你也可以不走,看操作2)
如果你不走这条边或者不可以走这条边(即你当前不在x或y上),那么你需要付出r的代价询问如果要从u点开始,按顺序处理完编号从l到r的边之后到达v点的最小代价,如果不能到达v,那么输出-1。
边和点的编号从1开始。

Data Constraint

对于20%的数据,n<=10,m<=1000,q<=1000
对于40%的数据,n<=10,m<=10000,q<=30000
对于60%的数据,n<=20,m<=10000,q<=30000
对于80%的数据,n<=25,m<=10000,q<=200000
对于100%的数据,n<=30,m<=20000,q<=200000

Solution

分治。mid左边设l[x][y][z]表示x~mid中从y开始z结束的最短路。mid右边设r[x][y][z]表示mid+1~x中从y开始z结束的最短路。更新一次是O(N^2)的。对于跨过mid的询问直接枚举中间点更新即可。时间复杂度O(n^2mlogm+n*q)。

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=2e5+5,maxn1=2e4+5;
struct code{
    int x,y,z,k,id;
}q[maxn],a[maxn],d[maxn];
int f[maxn1][31][31],b[maxn];
int n,m,qq,i,t,j,k,x,y,z;
void dg(int l,int r,int x,int y){
    if (x>y) return;
    if (l==r){
        if (a[l].x>a[l].y) swap(a[l].x,a[l].y);
        for (i=x;i<=y;i++){
            b[q[i].id]=1e9;
            if (q[i].x>q[i].y) swap(q[i].x,q[i].y);
            if (q[i].x==a[l].x && q[i].y==a[l].y) b[q[i].id]=a[l].z;
            if (q[i].x==q[i].y) b[q[i].id]=min(b[q[i].id],a[l].k);
        }
        return;
    }
    int mid=(l+r)/2,mi,mx;
    memset(f[mid],127,sizeof(f[mid]));
    f[mid][a[mid].y][a[mid].x]=f[mid][a[mid].x][a[mid].y]=a[mid].z;
    for (i=1;i<=n;i++) f[mid][i][i]=min(a[mid].k,f[mid][i][i]);
    for (i=mid-1;i>=l;i--){
        for (j=1;j<=n;j++)
            for (k=1;k<=n;k++)
                f[i][j][k]=f[i+1][j][k]+a[i].k;
        for (j=1;j<=n;j++)f[i][a[i].y][j]=min(f[i][a[i].y][j],f[i+1][a[i].x][j]+a[i].z);
        for (j=1;j<=n;j++)f[i][a[i].x][j]=min(f[i][a[i].x][j],f[i+1][a[i].y][j]+a[i].z);
    }
    memset(f[mid+1],127,sizeof(f[mid]));
    f[mid+1][a[mid+1].y][a[mid+1].x]=f[mid+1][a[mid+1].x][a[mid+1].y]=a[mid+1].z;
    for (i=1;i<=n;i++) f[mid+1][i][i]=min(a[mid+1].k,f[mid+1][i][i]);
    for (i=mid+2;i<=r;i++){
        for (j=1;j<=n;j++)
            for (k=1;k<=n;k++)
                f[i][j][k]=f[i-1][j][k]+a[i].k;
        for (j=1;j<=n;j++)f[i][j][a[i].y]=min(f[i][j][a[i].y],f[i-1][j][a[i].x]+a[i].z);
        for (j=1;j<=n;j++)f[i][j][a[i].x]=min(f[i][j][a[i].x],f[i-1][j][a[i].y]+a[i].z);
    }
    mi=x-1;mx=y+1;
    for (i=x;i<=y;i++)
        if (q[i].z<=mid && q[i].k>mid){
            b[q[i].id]=1e9;
            for (j=1;j<=n;j++)
                if (f[q[i].z][q[i].x][j]<1e9 && f[q[i].k][j][q[i].y]<1e9)b[q[i].id]=min(b[q[i].id],f[q[i].z][q[i].x][j]+f[q[i].k][j][q[i].y]);
        }else if (q[i].k<=mid) d[++mi]=q[i];
        else d[--mx]=q[i];
    for (i=x;i<=mi;i++)q[i]=d[i];for (i=mx;i<=y;i++) q[i]=d[i];
    dg(l,mid,x,mi);dg(mid+1,r,mx,y);
}
int main(){
    freopen("graph.in","r",stdin);freopen("graph.out","w",stdout);
    scanf("%d%d%d",&n,&m,&qq);memset(b,255,sizeof(b));
    for (i=1;i<=m;i++)scanf("%d%d%d%d",&a[i].x,&a[i].y,&a[i].z,&a[i].k);
    for (i=1;i<=qq;i++)scanf("%d%d%d%d",&q[i].x,&q[i].y,&q[i].z,&q[i].k),q[i].id=i;
    dg(1,m,1,qq);
    for (i=1;i<=qq;i++)
        if (b[i]!=1e9&&b[i]>=0)printf("%d\n",b[i]);
        else printf("-1\n");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值