【2018.6.29NOIP模拟】T3边的处理side 【分治】*

【2018.6.29NOIP模拟】T3边的处理side


【问题描述】

有一个 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)。
2、如果你不走这条边或者不可以走这条边(即你当前不在 x 或 y 上),那么你需要付出r 的代价。
询问如果要从 u 点开始,按顺序处理完编号从 l 到 r 的边之后到达 v 点的最小代价,如果不能到达 v,那么输出-1。
边和点的编号从 1 开始。

【输入格式】

第一行三个数表示 n,m,q。
接下来 m 行,每行四个整数 x,y,c,r 描述一条边。
接下来 q 行,每行四个整数 u,v,l,r 描述一组询问。

【输出格式】

输出共 q 行,每行一个数表示对应询问的答案。

【样例输入 1】

5 5 3
1 4 4 5
4 1 6 1
2 1 2 9
2 5 1 0
1 5 2 5
2 2 2 4
5 4 5 5
1 5 2 5

【样例输出 1】

10
-1
9

【样例输入 2】

4 8 6
2 4 5 8
2 4 4 8
2 3 6 4
1 4 5 0
2 4 10 10
1 3 5 2
3 2 2 9
3 4 1 1
3 2 1 5
3 1 2 2
1 1 1 7
2 3 2 4
3 3 1 7
1 2 2 5

【样例输出 2】

32
-1
41
14
36
27

【数据规模与约定】

对于 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。
对于 100%的数据:c,r 的取值范围是[0,10000]。


考场上写的20分的暴力DP

正解是分治:
假设当前在处理分治区间[L,R],设mid=(L+R)/2,处理询问区间跨过了 mid 的询问,
然后再递归左右区间进行处理
现在考虑怎么求跨过mid的询问
我们可以用DP得到lef[i][x][y]表示从x出发,处理了从i到mid的边之后到达y的最小代价,以及rig[i][x][y]表示从x出发,处理了从 mid+1到i的边之后到达y的最小代价,那么查询的时候只要枚举一下中间经过的点就好了
现在考虑如何进行DP:
如果不考虑经过新的边,我们只需要用上一层的DP值枚举u和v直接继承过来就行,如果考虑利用新加入的边进行更新,这和个时候我们只用枚举一个节点u到e.u/e.v更新u到e.v/e.u,然后我们这样就可以处理不经过mid的左右子区间答案
现在我们考虑怎么更新所有跨过mid边的询问,我们可以枚举1到n所有的节点然后把左右区间的值拼起来更新答案就好了
然后对于左右区间,我们只需要处理没有经过mid边的询问分别递归处理就好了

因为递归只会有 l o g m 层,每次递归的基本DP是 m n 2 的,这里的时间复杂度是 n 2 l o g m m 的,然后每一层我们需要确定有哪些区间是跨过mid的,在这里上线是 q l o g m 的,又因为每一层我们需要枚举节点来更新我们需要的询问,这里的复杂度是 q n 的,所以总复杂度是 O ( n 2 l o g m m + q l o g m + q n )


#include<bits/stdc++.h>
using namespace std;
#define MAXN 40
#define MAXM 20010
#define MAXQ 200010
#define INF 0x3f3f3f3f
struct Query{int u,v,l,r,id;}Q[MAXQ],tmp[MAXQ];
struct Edge{int x,y,c,r;}E[MAXM];
int n,m,q;
int ans[MAXQ];
int lef[MAXM][MAXN][MAXN];
int rig[MAXM][MAXN][MAXN];
void solve(int l,int r,int ql,int qr){
    if(ql>qr)return;
    if(l==r){
        for(int i=ql;i<=qr;i++){
            if(E[l].x==Q[i].u&&E[l].y==Q[i].v)ans[Q[i].id]=E[l].c;//只计算E[l] 
            if(E[l].y==Q[i].u&&E[l].x==Q[i].v)ans[Q[i].id]=E[l].c;
            if(Q[i].u==Q[i].v)ans[Q[i].id]=min(ans[Q[i].id],E[l].r);
        }
        return;
    }
    int mid=(l+r)>>1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            lef[mid+1][i][j]=rig[mid][i][j]=INF;
    for(int i=1;i<=n;i++)
        lef[mid+1][i][i]=rig[mid][i][i]=0;
    for(int i=mid;i>=l;i--){
        for(int u=1;u<=n;u++)
            for(int v=1;v<=n;v++)
                lef[i][u][v]=lef[i+1][u][v]+E[i].r;
        for(int u=1;u<=n;u++){
            lef[i][u][E[i].x]=min(lef[i][u][E[i].x],lef[i+1][u][E[i].y]+E[i].c);
            lef[i][u][E[i].y]=min(lef[i][u][E[i].y],lef[i+1][u][E[i].x]+E[i].c);
        }
    }
    for(int i=mid+1;i<=r;i++){
        for(int u=1;u<=n;u++)
            for(int v=1;v<=n;v++)
                rig[i][u][v]=rig[i-1][u][v]+E[i].r;
        for(int u=1;u<=n;u++){
            rig[i][u][E[i].x]=min(rig[i][u][E[i].x],rig[i-1][u][E[i].y]+E[i].c);
            rig[i][u][E[i].y]=min(rig[i][u][E[i].y],rig[i-1][u][E[i].x]+E[i].c);
        }
    }
    for(int i=ql;i<=qr;i++)
        if(Q[i].l<=mid&&mid<Q[i].r)
            for(int k=1;k<=n;k++)
                ans[Q[i].id]=min(ans[Q[i].id],lef[Q[i].l][k][Q[i].u]+rig[Q[i].r][k][Q[i].v]);
    int new_l=ql-1,new_r=qr+1;
    for(int i=ql;i<=qr;i++)tmp[i]=Q[i];
    for(int i=ql;i<=qr;i++)if(tmp[i].r<=mid)Q[++new_l]=tmp[i];//**边界 
    for(int i=qr;i>=ql;i--)if(tmp[i].l>mid)Q[--new_r]=tmp[i];//**ql qr 
    solve(l,mid,ql,new_l);
    solve(mid+1,r,new_r,qr);
}
int main(){
    memset(ans,0x3f,sizeof(ans));
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=m;i++)scanf("%d%d%d%d",&E[i].x,&E[i].y,&E[i].c,&E[i].r);
    for(int i=1;i<=q;i++)scanf("%d%d%d%d",&Q[i].u,&Q[i].v,&Q[i].l,&Q[i].r),Q[i].id=i;
    solve(1,m,1,q);
    for(int i=1;i<=q;i++)cout<<((ans[i]==INF)?-1:ans[i])<<"\n";
    return 0;
} 

猜你喜欢

转载自blog.csdn.net/dream_maker_yk/article/details/80859296