JZOJ 遨游

Description
MWH寒假外出旅游,来到了S国。S国划分为N个省,第i个省有Ti座城市,编号分别为Ci1,Ci2,……CiTi(各省城市编号不会重复)。所有城市间有M条双向的道路连接,从任意一个城市出发,可到达一切城市,每条道路均须收费。
此时恰逢春运期间,S国交通运输局采取了优惠措施。当一条路的路费在[L…R]区间时,可免去。同时,每个省也有优惠措施,第i个省内的每条道路路费收其Xi%,连接第i个省和第j个省的每条道路路费收其(Xi%+Xj%)/2。
MWH想从城市s走到城市t,请求出一对L,R,确保:

  1. MWH能免费到达目的地;
  2. L≤R;
  3. L、R均为整数;
  4. L尽可能地大,R在满足L最大的前提下最小。

注意:因每条道路由各省的交通运输局直接管辖,所以每条道路的路费必须先得到省级优惠,再得到国家级优惠。

Input
第一行两个整数N,M。
接下来M行,每行三个整数,u、v、w,表示连接u、v的道路需收费w。
接下来N行,第i+M+1行有一个整数Ti,后面Ti个整数,分别是Ci1…CiTi(所有城市编号保证按正整数顺序给出1… Ti)。
下一行N个整数X1…Xi。
最后一行,两个整数,s、t。
Output
一行两个整数,如题,L和R。
Sample Input
3 7
1 2 3
5 2 8
1 3 7
5 4 5
2 4 9
3 5 10
3 4 2
2 1 2
1 3
2 4 5
30 50 60
1 5

Sample Output
2 6

做的第一道双重二分的题。
要得到一个区间LR,使得R尽可能小,L尽可能大,然后在这段区间内的公路全部免费。我们要使得MWH免费游行,那我们要做的就是每局LR,使得可以走的公路免费。
那么枚举,仔细想想可以发现这道题具有单调性,当某一个值能走就把它增大或减小,当然另一边也是。所以我们可以二分枚举L和R,使得它满足题目要求。
一开始当然是建图,然后首先枚举L,看下界能不能达标,不能就把它减小,若是达标了,就枚举R原理同上,然后LR区间出来了以后,就从s dfs去跑,看从s能不能跑到t,可以的话就继续按照上面的来说继续去做。

#include<list>
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 100005
using namespace std;
struct node
{
    int u,v;
    double w;
}ed[N*2];
struct biao
{
    int v;
    double val;
};
list<biao>lis[N];
int dis[N],po[N],m,n,L,R,s,t;
double pe[N],w[N];
bool vis[N],ok;
void add(int wei,int uu,int vv,double zhi)
{
    ed[wei].u=uu;
    ed[wei].v=vv;
    ed[wei].w=zhi;
}
void ad(int uu,int vv,double zhi)
{
    lis[uu].push_back((biao){vv,zhi});
    lis[vv].push_back((biao){uu,zhi});
}
bool dfs(int xx,int zuo,int you)//dfs去跑的过程 
{
    if(xx==t)return true;
    vis[xx]=true;
    for(list<biao>::iterator it=lis[xx].begin();it!=lis[xx].end();it++)
    {
        if(it->val>=(double)zuo&&it->val<=(double)you&&!vis[it->v])
        {
            if(dfs(it->v,zuo,you))return true;
        }
    }
    return false;
}
int main()
{
    cin>>n>>m;//建图开始 
    for(int i=1;i<=m;i++)
    {
        int x,y;
        double z;
        scanf("%d %d %lf",&x,&y,&z);
        add(i,x,y,z);
    }
    for(int i=1;i<=n;i++)
    {
        int t;
        scanf("%d",&t);
        for(int j=1;j<=t;j++)
        {
            int xx;
            scanf("%d",&xx);
            po[xx]=i;
        }
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%lf",&pe[i]);
    }
    for(int i=1;i<=m;i++)
    {
        ad(ed[i].u,ed[i].v,(pe[po[ed[i].u]]+pe[po[ed[i].v]])/2.0*ed[i].w*0.01);
    }//建图结束 
    cin>>s>>t;
    int l1=0,r1=15000;
    while(l1<r1)//枚举L 
    {
    	int mid1=(l1+r1)>>1;
    	ok=false;
    	memset(vis,0,sizeof(vis));
    	if(dfs(s,mid1,(int)1e9))//枚举L的时候跑的话要把R设为无限大,这样可以让R不影响L,只要L能达标,再去跑R,这样就可以了 
    	{
    		int l2=mid1;
    		int r2=15000;
    		while(l2<r2)//枚举R 
    		{
    			int mid2=(l2+r2)>>1;
    			memset(vis,false,sizeof(vis));
    			if(dfs(s,mid1,mid2))
    			{
    				ok=true;
    				L=mid1;
    				R=mid2;
    				r2=mid2;
				}
				else l2=mid2+1;
			}
		}
		if(!ok)
		{
			r1=mid1;
	
		}
		else
		{
			l1=mid1+1;
		}
	}
    cout<<L<<" "<<R;
}

猜你喜欢

转载自blog.csdn.net/qq_37073764/article/details/83049382