歌名 - 歌手
0:00

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

    题目

    有一个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开始

    分析

    考虑分治,
    当前做到区间[l,r],mid=(l+r)/2
    设lf[i][x][y]表示在左区间中从x点开始,经过了i到mid的边,最后走到了y的最小代价
    类似的,rf[i][x][y]表示在左区间中从x点开始,经过了mid+1到i的边,最后走到了y的最小代价。
    那么我们将询问挂在l上,枚举i到mid,如果有询问的r在mid+1到r上,
    则对于询问\(<u,v,l,r>\)
    枚举中转点z,\(ans=min\{lf[l][u][z]+rf[r]][z][v]\}\)

    至于更新lf和rf
    如果当前区间为左区间,
    我们可以直接dp求出每条边到r的最小代价,
    对于由区间
    则求出从l到每条边的最小代价。

    那么总时间复杂度为\(O(qn(对于每个询问枚举中转点求答案)+qlogm(查看挂在每个点的询问)+n^2mlogm(更新lf和rf))\)

    #include <cmath>
    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    #include <map>
    const int maxlongint=2147483647;
    const int mo=1e9+7;
    const int N=200005;
    using namespace std;
    struct lzh
    {
    	int x,y,l,r;
    }qu[N*2],b[N*2];
    int n,m,q,next[N*2],last[N*2];
    long long f[20005][33][33],ans[N];
    long long min(long long x,long long y)
    {
    	return x<y?x:y;
    }
    void dg(int l,int r,int t)
    {
    	int mid=(l+r)>>1;
    	if(l!=r) dg(l,mid,0),dg(mid+1,r,1);
    	for(int x=l;x<=mid;x++)
    	{
    		for(int i=last[x];i;i=next[i])
    		{
    			if(qu[i].r>mid && qu[i].r<=r)
    			{
    				for(int y=1;y<=n;y++) ans[i]=min(ans[i],f[qu[i].l][qu[i].x][y]+f[qu[i].r][y][qu[i].y]);
    			}
    		}
    	}
    	if(t==2) return;
    	for(int i=l;i<=r;i++) memset(f[i],60,sizeof(f[i]));
    	if(!t)
    	{
    		for(int x=1;x<=n;x++)
    		{
    			f[r][x][x]=b[r].r;
    			if(b[r].x==x) f[r][b[r].y][x]=min(f[r][b[r].y][x],b[r].l);
    			if(b[r].y==x) f[r][b[r].x][x]=min(f[r][b[r].x][x],b[r].l);
    			for(int i=r-1;i>=l;i--)
    			{
    				for(int y=1;y<=n;y++) f[i][y][x]=f[i+1][y][x]+b[i].r;
    				int xx=b[i].x,yy=b[i].y;
    				f[i][yy][x]=min(f[i][yy][x],f[i+1][xx][x]+b[i].l);
    				f[i][xx][x]=min(f[i][xx][x],f[i+1][yy][x]+b[i].l);
    			}
    		}
    	}
    	else
    	{
    		for(int x=1;x<=n;x++)
    		{
    			f[l][x][x]=b[l].r;
    			if(b[l].x==x) f[l][x][b[l].y]=min(f[l][x][b[l].y],b[l].l);
    			if(b[l].y==x) f[l][x][b[l].x]=min(f[l][x][b[l].x],b[l].l);
    			for(int i=l+1;i<=r;i++)
    			{
    				for(int y=1;y<=n;y++) f[i][x][y]=f[i-1][x][y]+b[i].r;
    				int xx=b[i].x,yy=b[i].y;
    				f[i][x][yy]=min(f[i][x][yy],f[i-1][x][xx]+b[i].l);
    				f[i][x][xx]=min(f[i][x][xx],f[i-1][x][yy]+b[i].l);
    			}
    		}
    	}
    }
    int main()
    {
    	scanf("%d%d%d",&n,&m,&q);
    	for(int i=1;i<=m;i++) scanf("%d%d%d%d",&b[i].x,&b[i].y,&b[i].l,&b[i].r);
    	int sum[n+2],sum1[n+2];
    	memset(f,60,sizeof(f));
    	memset(ans,60,sizeof(ans));
    	for(int i=1,x,y,l,r;i<=q;i++)
    	{
    		scanf("%d%d%d%d",&qu[i].x,&qu[i].y,&qu[i].l,&qu[i].r);
    		if(qu[i].l==qu[i].r)
    		{
    			if(qu[i].x==qu[i].y) ans[i]=b[qu[i].l].r;
    			if(qu[i].x==b[qu[i].l].x && qu[i].y==b[qu[i].l].y || qu[i].x==b[qu[i].l].y && qu[i].y==b[qu[i].l].x) ans[i]=min(ans[i],b[qu[i].l].l);
    			continue;
    		}
    		next[i]=last[qu[i].l],last[qu[i].l]=i;
    	}
    	dg(1,m,2);
    	for(int i=1;i<=q;i++)
    	{
    		if(ans[i]>=f[0][0][0]) printf("-1\n");
    		else printf("%lld\n",ans[i]);
    	}
    }
    
    posted @ 2018-05-23 21:46  无尽的蓝黄  阅读(138)  评论(0编辑  收藏  举报