bzoj 4144: [AMPPZ2014]Petrol

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

Input
第一行包含三个正整数n,s,m(2<=s<=n<=200000,1<=m<=200000),表示点数、加油站数和边数。
第二行包含s个互不相同的正整数c[1],c[2],…cs,表示每个加油站。
接下来m行,每行三个正整数u[i],v[i],di,表示u[i]和v[i]之间有一条长度为d[i]的双向边。
接下来一行包含一个正整数q(1<=q<=200000),表示询问数。
接下来q行,每行包含三个正整数x[i],y[i],bi,表示一个询问。

Output
输出q行。第i行输出第i个询问的答案,如果可行,则输出TAK,否则输出NIE。

Sample Input
6 4 5

1 5 2 6

1 3 1

2 3 2

3 4 3

4 5 5

6 4 5

4

1 2 4

2 6 9

1 5 9

6 5 8

Sample Output
TAK

TAK

TAK

NIE

题目是我在八十中上课的时候zyb讲的 虽然上课一直在强调这题真的很简单 但是我是真的什么都不会 我好菜啊

题意:要求给出一条路径的长度 而你的车油量每次也是有限的 给出询问 每次询问都给出一个起点一个终点 询问是否可以从起点走到终点

首先 可以通过分析知道 两个点 如果他们最近的 加油站不同 那么他们的最优方案有可能是从这走过来的的 但是不满足这个条件一定不优 所以我首先把每个加油点的代价设为0 然后跑dijkstra

这样我就可以知道 所有点距离他最近的加油站是哪里了 然后 我就接下来枚举每条边 然后看他们两端的距离最近的加油站是否相同 如果不相同 那么他们之间到达的最小代价我就可以算出来了

就是两边到加油站的距离+中间路的权值 因为一定是先到最近的加油站在去别的地方可能形成最优解 由于题目数据可能不连通 那么所以我需要设一个超级根 然后把没有联通的加油站和他相连 权值设成无穷 然后建立一棵最小生成树出来 每次询问的时候我倍增lca求出两点间最大值 然后和我油量的大小比较然后输出即可

#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define pa pair<int,int>
#define N 220000
#define inf 0x3f3f3f3f
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0;char ch=gc();
    while(ch<'0'||ch>'9') ch=gc();
    while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=gc();}
    return x;
}
struct node{
    int x,y,z,next;
}data[N<<1],data1[N],data2[N<<1];bool flag[N];
int num,h[N],fa[N][30],mx[N][30],dis[N],from[N],father[N],c[N],n,s,m,root,Log[N],dep[N]; 
priority_queue<pa,vector<pa>,greater<pa> >q;
inline bool cmp(node a,node b){return a.z<b.z;}
inline int find(int x){return father[x]==x?x:father[x]=find(father[x]);}
inline void insert1(int x,int y,int z){
    data2[++num].y=y;data2[num].z=z;data2[num].next=h[x];h[x]=num;
    data2[++num].y=x;data2[num].z=z;data2[num].next=h[y];h[y]=num;
}
inline void dfs(int x){
    for (int i=h[x];i;i=data2[i].next){
        int y=data2[i].y;if (fa[x][0]==y) continue;fa[y][0]=x;dep[y]=dep[x]+1;mx[y][0]=data2[i].z;
        for (int j=1;j<=Log[dep[y]];++j) fa[y][j]=fa[fa[y][j-1]][j-1],mx[y][j]=max(mx[y][j-1],mx[fa[y][j-1]][j-1]);
        dfs(y);
    }
}
inline int lca(int x,int y){
    if (dep[x]<dep[y]) swap(x,y);
    int dis=dep[x]-dep[y];int ans=0;
    for (int i=0;i<=Log[dis];++i)
        if (dis&(1<<i)) ans=max(ans,mx[x][i]),x=fa[x][i];
    if (x==y) return ans;
    for (int i=25;i>=0;--i)
        if (fa[x][i]!=fa[y][i]) ans=max(ans,max(mx[x][i],mx[y][i])),x=fa[x][i],y=fa[y][i];
    ans=max(ans,max(mx[x][0],mx[y][0]));return ans;
}
int main(){
    //freopen("bzoj4144.in","r",stdin);
    n=read();s=read();m=read();memset(dis,0x3f,sizeof(dis));
    for (int i=1;i<=s;++i) {c[i]=read();q.push(make_pair(0,c[i]));dis[c[i]]=0;from[c[i]]=c[i];}
    for (int i=1;i<=m;++i){
        int x=read(),y=read(),z=read();
        data[++num].y=y;data[num].z=z;data[num].next=h[x];h[x]=num;data[num].x=x;
        data[++num].y=x;data[num].z=z;data[num].next=h[y];h[y]=num;data[num].x=y;
    }
    while(!q.empty()){
        int x=q.top().second;q.pop();if (flag[x]) continue;flag[x]=1;
        for (int i=h[x];i;i=data[i].next){
            int y=data[i].y,z=data[i].z;
            if (dis[x]+z<dis[y]){
                dis[y]=dis[x]+z;from[y]=from[x];q.push(make_pair(dis[y],y));
            }
        }
    }int cnt=0;//for (int i=1;i<=n;++i) printf("%d ",dis[i]);printf("\n");
    for (int i=1;i<=m<<1;i+=2){
        if(from[data[i].x]!=from[data[i].y]){
            data1[++cnt].x=from[data[i].x];data1[cnt].y=from[data[i].y];data1[cnt].z=data[i].z+dis[data[i].x]+dis[data[i].y];
        }
    }sort(data1+1,data1+cnt+1,cmp);memset(h,0,sizeof(h));
    //for (int i=1;i<=cnt;++i) printf("%d %d %d\n",data1[i].x,data1[i].y,data1[i].z);
    for (int i=1;i<=n;++i) father[i]=i;num=0;int tot=0;
    for (int i=1;i<=cnt;++i){
        int x=find(data1[i].x),y=find(data1[i].y);if (x==y) continue;
        father[x]=y;insert1(data1[i].x,data1[i].y,data1[i].z);if(++tot==n-1) break;
    }root=n+1;Log[0]=-1;for (int i=1;i<=n;++i) Log[i]=Log[i>>1]+1;
    for (int i=1;i<=s;++i) if (find(c[i])==c[i]) insert1(c[i],root,inf);
    dfs(root);int qq=read();
    for (int i=1;i<=qq;++i){
        int x=read(),y=read(),z=read();
        int ans=lca(x,y);
        if (z>=ans) printf("TAK\n");else printf("NIE\n");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值