bzoj 4144 [AMPPZ2014]Petrol 最短路+最小生成树+倍增

20 篇文章 0 订阅
3 篇文章 0 订阅

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

很好的noip前复习题,顺带 练练看长代码的能力。

然后这题思路很简单:
容易观察出来 这是一个只跟加油站有关系的 故事。
所以呢 我们把 关键点之间的最短路建边
生成最小生成树
查询就是树上倍增,答案统计边权最大的。

ps 有个坑:就是图有可能不连通 所以 dfs不能只dfs一个点

上代码【今天我缩行了】【捂脸】【捂不住】

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
//by mars_ch
int n,s,m;
struct data{
    int f,t,w,nxt;
}e[200005*2];
struct node{
    int u,v,w;
}bian[200005*2];
struct orz{
    int f,t,w,nxt;
}ee[200005*2];
int head[200005],tol;
int first[200005],tot;
queue<int> q;
int inq[200005],dis[200005],fa[200005],vis[200005],r[200005],p,cnt,root[200005],num,from[200005];
int deep[200005],f[200005][20],g[200005][20];
int find(int x){
    if(fa[x] == x) return x;
    return fa[x]=find(fa[x]);
}
bool cmp(node a,node b){
    return a.w<b.w;
}
void add(int a,int b,int c){
    e[tot].f=a,e[tot].t=b;
    e[tot].w=c,e[tot].nxt=first[a];
    first[a]=tot++;
}
void addd(int a,int b,int c){
    ee[tol].f=a,ee[tol].t=b;
    ee[tol].w=c,ee[tol].nxt=head[a];
    head[a]=tol++;
}
void spfa(){
    while(!q.empty()){
        int u=q.front();
        q.pop();
        inq[u]=0;

        for(int i=first[u];i!=-1;i=e[i].nxt){
            int t=e[i].t;
            if(dis[t]>dis[u]+e[i].w){
                from[t]=from[u];
                dis[t]=dis[u]+e[i].w;
                if(!inq[t]){
                    inq[t]=1;
                    q.push(t);
                }
            }
        }
    }

}
void dfs(int x,int fat){
    for(int i=1;i<=18;i++){
        f[x][i]=f[f[x][i-1]][i-1];
        g[x][i]=max(g[x][i-1],g[f[x][i-1]][i-1]);
    }
    for(int i=head[x];i!=-1;i=ee[i].nxt){
        int t=ee[i].t;
        if(t == fat) continue;
        deep[t]=deep[x]+1;
        f[t][0]=x,g[t][0]=ee[i].w;
        dfs(t,x);
    }
}
int lca(int x,int y){
    int ans=0;
    if(deep[x]<deep[y])swap(x,y);
    for(int i=18;i!=-1;i--){
        if(deep[x]-(1<<i)>=deep[y]){
            ans=max(ans,g[x][i]);
            x=f[x][i];
        }
    }
    if(x==y)return ans;
    for(int i=18;i!=-1;i--){
        if(f[x][i]!=f[y][i]){
            ans=max(ans,max(g[x][i],g[y][i]));
            x=f[x][i],y=f[y][i];
        }
    }
    return max(ans,max(g[x][0],g[y][0]));
}
int main()
{
    scanf("%d%d%d",&n,&s,&m);
    memset(first,-1,sizeof(first));
    memset(head,-1,sizeof(head));
    memset(dis,0x3f,sizeof(dis));
    for(int i=1;i<=n;i++){
        fa[i]=i;
    }
    for(int i=1;i<=s;i++){
        int a;
        scanf("%d",&a);
        from[a]=a;
        dis[a]=0,inq[a]=1;
        q.push(a);
    }
    for(int i=1;i<=m;i++){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c),add(b,a,c);
    }
    spfa();
    //printf("%d\n\n\n",from[4]); 
    for(int i=1;i<=m;i++)
    {
        int x=e[i*2-1].f,y=e[i*2-1].t;
        if(from[x]!=from[y])
        {
            bian[++cnt].u=from[x],bian[cnt].v=from[y];
            bian[cnt].w=dis[x]+dis[y]+e[i*2-1].w;
        }
    }
    /*for(int i=1;i<=cnt;i++)
    {
        printf("%d %d %d\n",bian[i].u,bian[i].v,bian[i].w);
    }*/
    sort(bian+1,bian+cnt+1,cmp);
    for(int i=1;i<=cnt;i++){
        int fu=find(bian[i].u),fv=find(bian[i].v);
        if(fu!=fv){
            fa[fu]=fv;
            addd(bian[i].u,bian[i].v,bian[i].w);
            addd(bian[i].v,bian[i].u,bian[i].w);
        }
    }
    for(int i=1;i<=n;i++){
        if(dis[i] == 0){
            if(!vis[find(i)]){
                vis[find(i)]=1;
                root[++num]=i;
            }
         } 
    }
    for(int i=1;i<=num;i++){
        dfs(root[i],-1);
    }
    int q;
    scanf("%d",&q);
    for(int i=1;i<=q;i++){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        if(find(x)!=find(y)) puts("NIE");
        else if(z>=lca(x,y)) puts("TAK");
        else puts("NIE");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值