bzoj4144【AMPPZ2014】Petrol


 

  • 题解:

       首先注意到起点和终点都是加油站;

         假设中途经过某个非加油站的点u,u连到v,离u最近的加油站是x,那么从u到x加油后回到u,再到v一定不比直接从u到v差;

       因为u一定从某个加油站来,设最后经过的加油站为y,u点油量为B1 = b - dis(y,u),而如果u不可以走到x一定不能走到其他任何加油站自然也到不了终点,如果可以到x加满油也一定可以再从x回来,油量为B2 = b-dis(x,u)  , 因为dis(y,u) >= dis(x,u)所以B1 <= B2 ;

       考虑重新构图:nr[x]表示离x最近的加油站,dis[x]表示x和nr[x]的距离,可以用多源点dijkstra处理出所有nr[x]和dis[x];

       对于原图中边(u,v) 连边(nr[u] , nr[v] , dis[u] + dis[v] + w(u,v)   ) ;

       这就变成了一个图,只选<=b 的边问两点连通性,可以离线或者用kruskal重构树做;


 

 1 #include<bits/stdc++.h>
 2 #define mk make_pair
 3 #define fir first
 4 #define sec second 
 5 using namespace std;
 6 const int N=200010;
 7 int n,m,k,s,c[N],dis[N],nr[N],vis[N],fa[N],o,hd[N],cnt,ans[N];
 8 struct Edge{int u,v,nt,w;}E[N<<1],e[N],Q[N];
 9 void adde(int u,int v,int w){
10     E[o]=(Edge){u,v,hd[u],w};hd[u]=o++;
11     E[o]=(Edge){v,u,hd[v],w};hd[v]=o++;
12 }
13 typedef pair<int,int> pii;
14 priority_queue<pii,vector<pii>,greater<pii> > q;
15 void dijkstra(){
16     memset(dis,0x3f,sizeof(dis));
17     for(int i=1;i<=s;i++)q.push(mk(dis[c[i]]=0,c[i])),nr[c[i]]=c[i];
18     while(!q.empty()){
19         int u=q.top().sec;q.pop();
20         if(vis[u])continue;
21         vis[u]=1;
22         for(int i=hd[u];~i;i=E[i].nt){
23             int v=E[i].v;
24             if(dis[v]>dis[u]+E[i].w){
25                 dis[v]=dis[u]+E[i].w;
26                 nr[v]=nr[u];
27                 if(!vis[v])q.push(mk(dis[v],v)); 
28             }
29         }
30     }
31 }
32 bool cmp(const Edge&A,const Edge&B){return A.w<B.w;}
33 int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
34 int main(){
35     freopen("bzoj4144.in","r",stdin);
36     freopen("bzoj4144.out","w",stdout);
37     memset(hd,-1,sizeof(hd));
38     scanf("%d%d%d",&n,&s,&m);
39     for(int i=1;i<=s;i++)scanf("%d",&c[i]);
40     for(int i=1;i<=m;i++){
41         int u,v,w;
42         scanf("%d%d%d",&u,&v,&w);
43         adde(u,v,w);
44     }
45     scanf("%d",&k);
46     for(int i=1;i<=k;i++){scanf("%d%d%d",&Q[i].u,&Q[i].v,&Q[i].w),Q[i].nt=i;}
47     dijkstra();
48     for(int i=0;i<o;i+=2){
49         int u=E[i].u,v=E[i].v;
50         if(nr[u]!=nr[v])e[++cnt]=(Edge){nr[u],nr[v],0,E[i].w+dis[u]+dis[v]};
51     }
52     for(int i=1;i<=n;i++)fa[i]=i;
53     sort(e+1,e+cnt+1,cmp);
54     sort(Q+1,Q+k+1,cmp);
55     for(int i=1,j=1;i<=k;i++){
56         while(j<=cnt&&e[j].w<=Q[i].w){
57             int fx=find(e[j].u),fy=find(e[j].v);
58             if(fx!=fy)fa[fx]=fy;
59             j++;
60         }
61         ans[Q[i].nt] = find(Q[i].u)==find(Q[i].v);
62     }
63     for(int i=1;i<=k;i++){puts(ans[i]?"TAK":"NIE");}
64     return 0;
65 }
bzoj4144

 

转载于:https://www.cnblogs.com/Paul-Guderian/p/10204966.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值