zoukankan      html  css  js  c++  java
  • 【JZOJ4858】【GDOI2017模拟11.4】Walk

    题目描述

    在比特镇一共有n 个街区,编号依次为1 到n,它们之间通过若干条单向道路连接。
    比特镇的交通系统极具特色,除了m 条单向道路之外,每个街区还有一个编码vali,不同街区可能拥有相同的编码。如果val_i and val_j = val_j,即val_i 在二进制下与val_j 做与运算等于val_j,那么也会存在一条额外的从i 出发到j 的单向道路。
    Byteasar 现在位于1 号街区,他想知道通过这些道路到达每一个街区最少需要多少时间。因为比特镇的交通十分发达,你可以认为通过每条道路都只需要1 单位时间。

    数据范围

    这里写图片描述

    =w=

    暴力连边后做最短路,但这样显然边数太多。
    对于一个点i,他可以额外连向j,当且仅当val[i]&val[j]==val[j]。
    如果把这条边拆成(i,val[i]),(val[i],val[j]),(val[j],j),那么显然可以使用差分模型优化边数。

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define ll long long
    #define sqr(x) ((x)*(x))
    #define ln(x,y) int(log(x)/log(y))
    #define point(x) ((x)+maxa-1)
    using namespace std;
    const char* fin="walk.in";
    const char* fout="walk.out";
    const int inf=0x7fffffff;
    const int maxn=1548576,maxm=maxn*4,maxa=1<<20;
    int n,m,i,j,k;
    int fi[maxn],la[maxm],ne[maxm];
    int va[maxm];
    int tot,a[maxn];
    int head,tail,b[maxn*3],dis[maxn];
    bool bz[maxn];
    void add_line(int a,int b,int c){
        tot++;
        ne[tot]=fi[a];
        la[tot]=b;
        va[tot]=c;
        fi[a]=tot;
    }
    void add(int v,int v1){
        if (dis[v]>v1){
            dis[v]=v1;
            if (!bz[v]){
                bz[v]=true;
                b[++tail]=v;
            }
        }
    }
    void spfa(int v){
        int i,j,k;
        head=tail=0;
        memset(dis,127,sizeof(dis));
        add(v,0);
        while (head++<tail){
            if (b[head]<maxa)
                for (i=0;i<20;i++)
                    if (b[head]&(1<<i)) add(b[head]^(1<<i),dis[b[head]]);
            for (k=fi[b[head]];k;k=ne[k]) add(la[k],dis[b[head]]+va[k]);
            bz[b[head]]=false;
        }
    }
    int main(){
        freopen(fin,"r",stdin);
        freopen(fout,"w",stdout);
        scanf("%d%d",&n,&m);
        for (i=1;i<=n;i++){
            scanf("%d",&k);
            add_line(point(i),k,1);
            add_line(k,point(i),0);
        }
        for (i=1;i<=m;i++){
            scanf("%d%d",&j,&k);
            add_line(point(j),point(k),1);
        }
        spfa(point(1));
        for (i=1;i<=n;i++) if (dis[point(i)]>2000000000) printf("-1
    ");
        else printf("%d
    ",dis[point(i)]);
        return 0;
    }

    启发

    对于一个相同条件来进行连边的,考虑拆边来达到差分模型优化边数。

  • 相关阅读:
    C++ 修改常量的值
    Android Studio 使用入门
    Ubuntu14.04下配置固定IP
    vi/vim 按键说明
    linux下文件夹的创建、复制、剪切、重命名、清空和删除命令
    linux中的find命令——查找文件名
    shell 脚本编写基础
    linux C程序中获取shell脚本输出(如获取system命令输出)
    vi 技巧
    理解Linux中的shutdown、poweroff、halt和reboot命令
  • 原文地址:https://www.cnblogs.com/hiweibolu/p/6714832.html
Copyright © 2011-2022 走看看