zoukankan      html  css  js  c++  java
  • 【BZOJ】4144: [AMPPZ2014]Petrol

    题意

    给定一个(n)个点、(m)条边的带权无向图,其中有(s)个点是加油站。每辆车都有一个油量上限(b),即每次行走距离不能超过(b),但在加油站可以补满。(q)次询问,每次给出(x,y,b),表示出发点是(x),终点是(y),油量上限为(b),且保证(x)点和(y)点都是加油站,请回答能否从(x)走到(y)。((2 le s le n le 200000, 1 le m le 200000)

    分析

    首先来分析如果只有一个询问,给出(x)点和(y)点问(x)是否能走到(y)。假设经过的非加油站的点集依次是(p_1, p_2, cdots, p_k),那么考虑从(p_1)走到(p_2),假设有边((p_1, p_2))(否则一定是从加油站走到(p_2)的),权值为(w)。如果我们从(p_1)走到最近的加油站(距离为(d(p_1)))加满油再回来,还剩的油为(b-d(p_1)),然后再走这条边。而如果直接从(p_1)走到(p_2),由于没有加油,此时在(p_1)的油量显然是(le b-d(p_1))的。显然加了油更优。

    题解

    根据分析,我们只需要按新赋值的边权w+d(u)+d(v)生成最小生成树来判断即可。对于询问,离线一下即可。

    #include <bits/stdc++.h>
    using namespace std;
    const int N=200005, M=200005;
    int ihead[N], cnt, n, m, s, p[N], h[N], c[N];
    struct E {
    	int next, to, w;
    }e[M<<1];
    struct ED {
    	int x, y, d;
    }ed[M];
    struct QU {
    	int x, y, b, id;
    }q[N];
    void add(int x, int y, int w) {
    	e[++cnt]=(E){ihead[x], y, w}; ihead[x]=cnt;
    	e[++cnt]=(E){ihead[y], x, w}; ihead[y]=cnt;
    }
    int find(int x) {
    	return x==p[x]?x:p[x]=find(p[x]);
    }
    typedef pair<int, int> pii;
    #define mkpii make_pair<int, int>
    priority_queue<pii, vector<pii>, greater<pii> > qu;
    int d[N], vis[N];
    void dij() {
    	memset(d, 0x7f, sizeof(int)*(n+1));
    	for(int i=0; i<s; ++i) {
    		d[c[i]]=0;
    		qu.push(mkpii(0, c[i]));
    	}
    	while(qu.size()) {
    		int x=qu.top().second;
    		qu.pop();
    		if(vis[x]) {
    			continue;
    		}
    		vis[x]=1;
    		for(int i=ihead[x]; i; i=e[i].next) {
    			int y=e[i].to;
    			if(d[y]>d[x]+e[i].w) {
    				d[y]=d[x]+e[i].w;
    				qu.push(mkpii(d[y], y));
    			}
    		}
    	}
    }
    inline bool cmp1(const ED &a, const ED &b) {
    	return a.d<b.d;
    }
    inline bool cmp2(const QU &a, const QU &b) {
    	return a.b<b.b;
    }
    int ans[N];
    int main() {
    	scanf("%d%d%d", &n, &s, &m);
    	for(int i=1; i<=n; ++i) {
    		p[i]=i;
    		h[i]=1;
    	}
    	for(int i=0; i<s; ++i) {
    		scanf("%d", &c[i]);
    	}
    	for(int i=0; i<m; ++i) {
    		scanf("%d%d%d", &ed[i].x, &ed[i].y, &ed[i].d);
    		add(ed[i].x, ed[i].y, ed[i].d);
    	}
    	dij();
    	for(int i=0; i<m; ++i) {
    		ed[i].d+=d[ed[i].x]+d[ed[i].y];
    	}
    	int Q;
    	scanf("%d", &Q);
    	for(int i=0; i<Q; ++i) {
    		scanf("%d%d%d", &q[i].x, &q[i].y, &q[i].b);
    		q[i].id=i;
    	}
    	sort(ed, ed+m, cmp1);
    	sort(q, q+Q, cmp2);
    	int now=0;
    	for(int i=0; i<Q; ++i) {
    		while(now<m && ed[now].d<=q[i].b) {
    			int fx=find(ed[now].x), fy=find(ed[now].y);
    			if(fx!=fy) {
    				if(h[fx]>h[fy]) {
    					swap(fx, fy);
    				}
    				p[fx]=fy;
    				h[fy]+=h[fy]==h[fx];
    			}
    			++now;
    		}
    		ans[q[i].id]=find(q[i].x)==find(q[i].y);
    	}
    	for(int i=0; i<Q; ++i) {
    		puts(ans[i]?"TAK":"NIE");
    	}
    	return 0;
    }
  • 相关阅读:
    Live2D 看板娘
    Live2D 看板娘
    Live2D 看板娘
    Live2D 看板娘
    Live2D 看板娘
    Live2D 看板娘
    Live2D 看板娘
    Live2D 看板娘
    Live2D 看板娘
    重读《深入理解Java虚拟机》三、Java虚拟机执行的数据入口(类文件结构)
  • 原文地址:https://www.cnblogs.com/iwtwiioi/p/4986381.html
Copyright © 2011-2022 走看看