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,确保:
- MWH能免费到达目的地;
- L≤R;
- L、R均为整数;
- 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;
}