题目大意:
题目链接:https://jzoj.net/senior/#main/show/5771
题目图片:
https://www.z4a.net/images/2018/09/22/Screenshot949650fe938ea13c.png
https://www.z4a.net/images/2018/09/22/Screenshot-11765113419a5a9a4.md.png
https://www.z4a.net/images/2018/09/22/2.md.png
https://www.z4a.net/images/2018/09/22/1.md.png
给出个省,每个神有自己的折扣,每个省有个城市。有条路,每条路连接两个城市和,原价是,但打折后的价格就是所在城市所在城市。
如果要从到的路程免费,那么就得有打折后价格在区间之间的路免费通行。求在最大的情况下且尽量小的。
思路:
可以二分和。
二分嵌套二分,第一重二分,第二重二分。
每次二分完之后,搜索一遍能否只走价格大于的路到达终点,如果可以,就二分。
每次二分之后,如果搜到可以只走价格大于且小于的路线的话,那么久将这一组答案记录下来。
最终输出记录的答案即可。
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#define N 101000
using namespace std;
int n,m,num,s,t,l1,r1,l2,r2,mid1,mid2,tot,L,R;
int u[N],v[N],pos[N],head[N];
double w[N],per[N];
bool ok,vis[N];
struct edge
{
int dis,to,next;
}e[N*2];
void add(int from,int to,double dis)
{
e[++tot].to=to;
e[tot].dis=dis;
e[tot].next=head[from];
head[from]=tot;
}
bool dfs(int x,int l,int r) //搜索是否有方法可以在限制条件下到达t
{
if (x==t) return true;
if (vis[x]) return false;
vis[x]=1;
for (int i=head[x];~i;i=e[i].next)
if (e[i].dis>=(double)l&&e[i].dis<(double)r)
if (dfs(e[i].to,l,r)) return true;
return false;
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
scanf("%d%d%lf",&u[i],&v[i],&w[i]);
for (int i=1;i<=n;i++)
{
scanf("%d",&num);
int x;
for (int j=1;j<=num;j++)
{
scanf("%d",&x);
pos[x]=i;
}
}
for (int i=1;i<=n;i++)
scanf("%lf",&per[i]);
for (int i=1;i<=m;i++)
{
add(u[i],v[i],(per[pos[u[i]]]+per[pos[v[i]]])/2.0*w[i]*0.01);
add(v[i],u[i],(per[pos[u[i]]]+per[pos[v[i]]])/2.0*w[i]*0.01);
}
scanf("%d%d",&s,&t);
l1=0;
r1=15000;
while (l1<=r1) //二分L
{
mid1=(l1+r1)/2;
l2=mid1;
r2=15000;
ok=false;
memset(vis,0,sizeof(vis));
if (dfs(s,mid1,1e9))
while (l2<=r2) //二分R
{
mid2=(l2+r2)/2;
memset(vis,0,sizeof(vis));
if (dfs(s,mid1,mid2))
{
ok=true;
L=mid1;
R=mid2;
r2=mid2-1;
}
else l2=mid2+1;
}
if (ok) l1=mid1+1;
else r1=mid1-1;
}
printf("%d %d\n",L,R);
return 0;
}