JZOJ4893【NOIP2016提高A组集训第15场11.14】过河

17 篇文章 0 订阅

Description

这里写图片描述

Data Constraint

这里写图片描述

Solution

这道题很容易就想到是spfa。但问题就是怎样处理连边问题。普通的连边是O( N4 )的,显然空间和时间都不允许。我们可以将半径从大到小排序。显然加入对于一个点对(i,j),假如i选择了半径x,j选择半径y可以相切或相交,那么j选择半径>=y的也一定是可以的。所以我们只要向j满足相交的最低条件的y连一条边即可。那么我们怎样处理i连向比y大的半径呢?我们可以每个点由小半径向大半径连一条大半径费用减小半径费用的边。这样连的边数就会变为O( N3+N2 )。这时跑spfa就好了。

这里写图片描述

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define db double
#define ll long long
using namespace std;
const int maxn=255,maxn1=62505,maxn2=18800000;
struct code{
       int a,b;
}d[maxn];
int a[maxn],b[maxn],r[maxn],c[maxn],v[maxn1*100],d1[maxn1];
int first[maxn1],last[maxn2],next[maxn2],value[maxn2];
int n,i,t,j,k,l,m,p,x,y,test,num,r1,mid,q;
bool bz[maxn1];
db z;
bool cmp(code x,code y){
     return x.a>y.a || x.a==y.a && x.b<y.b;
}
ll sqr(ll x){
   return x*x;
} 
void lian(int x,int y,int z){
     last[++num]=y;next[num]=first[x];first[x]=num;value[num]=z;    
}
void spfa(){
     v[1]=i=0;j=1;
     memset(d1,127,sizeof(d1));d1[0]=0;bz[0]=true;q=d1[1];
     while (i<j){
           x=v[++i];
           for (t=first[x];t;t=next[t]){
                if (d1[last[t]]<=d1[x]+value[t]) continue;
                d1[last[t]]=d1[x]+value[t];
                if (!bz[last[t]]){
                    v[++j]=last[t],bz[v[j]]=true;
                    if (d1[v[j]]<d1[v[i+1]]) swap(v[j],v[i+1]);
                }
            }
           bz[x]=false;
     }
}
int main(){
    freopen("river.in","r",stdin);freopen("river.out","w",stdout);
    scanf("%d",&test);
    while (test){
          scanf("%d%d%d",&n,&m,&p);    
          for (i=1;i<=n;i++)
              scanf("%d%d",&a[i],&b[i]);
          for (i=1;i<=m;i++)
              scanf("%d%d",&d[i].a,&d[i].b);
          num=0;
          memset(first,0,sizeof(first));
          sort(d+1,d+m+1,cmp);
          t=1e9;l=0;
          for (i=1;i<=m;i++)
               if (t>d[i].b){
                  if (d[i].a==d[i-1].a) continue;
                  r[++l]=d[i].a,c[l]=d[i].b;
                  t=min(t,c[l]);
               }   
          m=l;
          for (i=1;i<=n;i++)
              for (j=1;j<=n;j++)
              if (i!=j){
                    l=m;
                    z=sqrt(sqr(a[i]-a[j])+sqr(b[i]-b[j]));
                  for (k=1;k<=m;k++){
                    while (r[k]+r[l]<z && l)  l--;
                    if (r[k]+r[l]>=z) lian((i-1)*m+k,(j-1)*m+l,c[l]);
                  }
              }
            for (i=1;i<=n;i++)
                for (j=m-1;j>=1;j--)
                    lian((i-1)*m+j+1,(i-1)*m+j,c[j]-c[j+1]);
          for (i=1;i<=n;i++)
              for (k=1;k<=m;k++){
                  if (r[k]<b[i])break;
                  lian(0,(i-1)*m+k,c[k]);
              }
          for (i=1;i<=n;i++)
              for (k=1;k<=m;k++){
                  if (r[k]<p-b[i])break;
                  lian((i-1)*m+k,n*m+1,0);
              }
          spfa();
          if (d1[n*m+1]!=q)printf("%d\n",d1[n*m+1]);
          else printf("impossible\n");
          test--;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值