JZOJ 5430 图

Description

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

Data Constraint

n<= 30 , m <=2104, q <=2105

Solution

离线,考虑分治。
假设当前在处理分治边区间[ L ,R],设 mid =( L +R)/ 2 ,处理询问区间跨过了mid的询问,可以用考虑动态规划设 lef [ i ][x][ y ]表示从x出发,处理了从 i mid的边之后到达 y 的最小代价,以及rig[ i ][x][ y ]表示从x出发,处理了从 mid + 1 i的边之后到达 y 的最小代价。
显然从lef[ i ][x][ y ]转移到lef[ i -1][ x ][y]、从 rig [ i ][x][ y ]转移到rig[ i +1][ x ][y]的时间复杂度都为O( n2 ),因而整个分治的dp转移复杂度为O( m log m *n2)。,那么查询的时候只要枚举一下在第 mid 条边的中转点就好了。
总时间复杂度 O (q n +q log m +m log m *n2)

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>

#define fo(i,j,l) for(int i=j;i<=l;i++)
#define fd(i,j,l) for(int i=j;i>=l;i--)

using namespace std;
typedef long long ll;
const ll N=31,M=22e3,K=10*M,maxn=1e14;
struct note{
    int x,y,l,r;
}t[M],qu[K];

int ne[K],la[M],lb[K];
ll dis[M][N][N],ans[K];
int n,m,j,k,l,i,o,p,q;

int read()
{
    int o=0; char ch=' ';
    for(;ch<'0'||ch>'9';ch=getchar());
    for(;ch>='0'&&ch<='9';ch=getchar())o=o*10+ch-48;
    return o;
}

ll min(ll a,ll b)
{if(a<b)return a;else return b;}

void divide(int l,int r,int op)
{
    if(l==r){
        fo(i,1,n)fo(j,1,n)dis[l][i][j]=maxn;
        int x=t[l].x,y=t[l].y;
        fo(j,1,n)dis[l][j][j]=t[l].r;
        dis[l][x][y]=dis[l][y][x]=t[l].l; 
        for(int y=la[l];y;y=ne[y]){
                int u=lb[y];
                if(qu[u].r==l)ans[u]=dis[l][qu[u].x][qu[u].y];
        }
        return;
    }
    int mid=(l+r)/2;
    divide(l,mid,0); divide(mid+1,r,1);
    fo(i,1,n)fo(j,1,n)dis[mid][i][j]=maxn;
    int x=t[mid].x,y=t[mid].y;
    dis[mid][x][y]=dis[mid][y][x]=t[mid].l; 
    fo(j,1,n)dis[mid][j][j]=min(t[mid].r,dis[mid][j][j]);
    fd(i,mid-1,l){
        int x=t[i].x,y=t[i].y;
        fo(j1,1,n)fo(j2,1,n)dis[i][j1][j2]=dis[i+1][j1][j2]+t[i].r;
        fo(j,1,n)dis[i][j][x]=min(dis[i+1][j][y]+t[i].l,dis[i][j][x]);
        fo(j,1,n)dis[i][j][y]=min(dis[i+1][j][x]+t[i].l,dis[i][j][y]);
    }
    fo(i,1,n)fo(j,1,n)dis[mid+1][i][j]=maxn;
    x=t[mid+1].x,y=t[mid+1].y;
    dis[mid+1][x][y]=dis[mid+1][y][x]=t[mid+1].l; 
    fo(j,1,n)dis[mid+1][j][j]=min(t[mid+1].r,dis[mid+1][j][j]);
    fo(i,mid+2,r){
        int x=t[i].x,y=t[i].y;
        fo(j1,1,n)fo(j2,1,n)dis[i][j1][j2]=dis[i-1][j1][j2]+t[i].r;
        fo(j,1,n)dis[i][j][x]=min(dis[i-1][j][y]+t[i].l,dis[i][j][x]);
        fo(j,1,n)dis[i][j][y]=min(dis[i-1][j][x]+t[i].l,dis[i][j][y]);
    }
    fo(i,l,mid){
    for(int y=la[i];y;y=ne[y]){
            int u=lb[y];
            if(qu[u].r>mid&&qu[u].r<=r){
                int xx=qu[u].x,yy=qu[u].y;
                int le=qu[u].l,ri=qu[u].r;
                ans[u]=maxn;
                fo(j,1,n)ans[u]=min(ans[u],dis[le][j][xx]+dis[ri][j][yy]);
            }
        }
    }
}

int main()
{
    cin>>n>>m>>q;
    fo(i,1,m)t[i].x=read(),t[i].y=read(),t[i].l=read(),t[i].r=read();
    fo(i,1,q)qu[i].x=read(),qu[i].y=read(),
    qu[i].l=read(),qu[i].r=read();
    fo(i,1,q)ne[++o]=la[qu[i].l],la[qu[i].l]=o,lb[o]=i;
    divide(1,m,1);
    fo(i,1,q)if(ans[i]>=maxn)puts("-1");else printf("%lld\n",ans[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值