bzoj 4144: [AMPPZ2014]Petrol

题意:

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

题解:

关键是要求出加油站之间的最小生成树,然后倍增求最大值。
用类似bzoj 4242的方法即可。
各种sb错误调了很久
code:

#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
const LL inf=(1LL<<60);
struct node{
    int x,y,next;
    LL c;
}a[400010],b[200010];int len=0,last[200010];
int n,s,m,fa[200010];
struct P{
    int x;LL dis;
    P() {}
    P(int a,LL b) {x=a;dis=b;}
};
struct trnode{
    int fa[21],dep;
    LL c[21];
}tr[200010];
bool operator < (P a,P b) {return a.dis>b.dis;}
priority_queue<P> q;
int belong[200010];
LL dis[200010];
bool vis[200010];
int findfa(int x) {return fa[x]==x?x:fa[x]=findfa(fa[x]);}
void ins(int x,int y,LL c)
{
    a[++len].y=y;a[len].c=c;
    a[len].next=last[x];last[x]=len;
}
void dij()
{
    while(!q.empty())
    {
        P t=q.top();q.pop();int x=t.x;
        if(vis[x]) continue;
        vis[x]=true;
        for(int i=last[x];i;i=a[i].next)
        {
            int y=a[i].y;
            if(dis[x]+(LL)a[i].c<dis[y])
            {
                dis[y]=dis[x]+(LL)a[i].c;belong[y]=belong[x];
                q.push(P(y,dis[y]));
            }
            else
                if(vis[y]) b[++len].x=belong[x],b[len].y=belong[y],b[len].c=(LL)(dis[x]+dis[y]+a[i].c);
        }
    }
}
void dfs(int x,int fa,LL c)
{
    tr[x].dep=tr[fa].dep+1;tr[x].fa[0]=fa;tr[x].c[0]=c;
    vis[x]=true;
    for(int i=1;(1<<i)<=tr[x].dep;i++)
    {
        tr[x].fa[i]=tr[tr[x].fa[i-1]].fa[i-1];
        tr[x].c[i]=max(tr[x].c[i-1],tr[tr[x].fa[i-1]].c[i-1]);
    }
    for(int i=last[x];i;i=a[i].next)
    {
        int y=a[i].y;
        if(y==fa) continue;
        dfs(y,x,a[i].c);
    }
}
LL findans(int x,int y)
{
    LL ans=0;
    if(tr[x].dep<tr[y].dep) swap(x,y);
    for(int i=19;i>=0;i--)
        if((1<<i)<=tr[x].dep-tr[y].dep)
        {
            ans=max(ans,tr[x].c[i]);
            x=tr[x].fa[i];
        }
    if(x==y) return ans;
    for(int i=19;i>=0;i--)
        if((1<<i)<=tr[x].dep&&tr[x].fa[i]!=tr[y].fa[i])
        {
            ans=max(ans,tr[x].c[i]);ans=max(ans,tr[y].c[i]);
            x=tr[x].fa[i];y=tr[y].fa[i];
        }
    ans=max(ans,tr[x].c[0]);ans=max(ans,tr[y].c[0]);
    return ans;
}
bool cmp(node a,node b) {return a.c<b.c;}
int main()
{
    scanf("%d %d %d",&n,&s,&m);
    for(int i=1;i<=n;i++) dis[i]=inf;
    memset(vis,false,sizeof(vis));
    for(int i=1;i<=s;i++)
    {
        int k;scanf("%d",&k);
        belong[k]=k;
        dis[k]=0;q.push(P(k,0LL));
    }
    for(int i=1;i<=m;i++)
    {
        int x,y;LL c;scanf("%d %d %lld",&x,&y,&c);
        ins(x,y,c);ins(y,x,c);
    }
    len=0;dij();
    int m=len;memset(last,0,sizeof(last));
    memset(vis,false,sizeof(vis));
    for(int i=1;i<=n;i++) fa[i]=i;
    sort(b+1,b+m+1,cmp);len=0;
    for(int i=1;i<=m;i++)
    {
        int x=findfa(b[i].x),y=findfa(b[i].y);
        if(x!=y)
        {
            ins(x,y,b[i].c),ins(y,x,b[i].c);
            fa[x]=y;
        }
    }
    tr[0].dep=-1;for(int i=1;i<=n;i++) if(belong[i]==i&&!vis[i]) dfs(i,0,inf);
    int Q;scanf("%d",&Q);
    while(Q--)
    {
        int x,y;LL B,t;scanf("%d %d %lld",&x,&y,&B);t=findans(x,y);
        if(B>=t) printf("TAK\n");
        else printf("NIE\n");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值