GalaxyOJ-1000 (縮點+最短路)

題目

http://www.gdfzoj.com/oj/contest/247/problems/3
題目
數據範圍

分析

  • 對於前四個數據,直接暴力建圖再 SPFA 一下最短路即可。
  • 分析一下,對於後面的數據,主要是建圖時“特殊邊”(就是根據與運算得出的邊)建的時候需要平方的複雜度,優化一下這裏即可。
  • 可以考慮弄完一個點的 dis[] 之後,直接把它能通過“特殊邊”到的點都更新一次。
  • 由於邊長都爲1,所以對於每個點肯定是第一次更新的值是最小的,弄“點權範圍”個 vector 存下點權爲它的點的編號,每次討論時 dfs 一下把它能一步到的點更新一下即可。(之前更新過的就不用換了)

程序

#include <cstdio>
#include <cstring>
#include <vector>
#define Add(x,y) (to[++num]=head[x],head[x]=num,V[num]=y)
#define For(x) for(int h=head[x],o=V[h]; h; o=V[h=to[h]])
using namespace std;
int head[200005],to[300005],V[300005],num;
int q[2000000],vis[200005],f[1100000],l,r;
int n,m,val[200005],dis[200005];
vector <int> a[1100000];

void dfs(int x,int ds){     //從某個 權值爲 x 的點 通過特殊路到的點,dis=ds 
    if (f[x]) return;
    f[x]=1;
    for (int i=20; i>=0; i--) if (x&(1<<i)) dfs(x^(1<<i),ds);
    if (!a[x].empty()) for (int i=0; i<a[x].size(); i++){
        int o=a[x][i];
        if (vis[o]) continue;
        vis[o]=1;
        dis[o]=ds;
        q[++r]=o;
    }
}

int main(){
    freopen("1.txt","r",stdin);
    scanf("%d%d",&n,&m);
    for (int i=1; i<=n; i++) scanf("%d",val+i),a[val[i]].push_back(i);
    for (int i=1,x,y; i<=m; i++) scanf("%d%d",&x,&y),Add(x,y);  
    memset(dis,0x7f,sizeof(dis)); dis[1]=0;
    for (q[l=r=0]=vis[1]=1; l<=r; l++){
        int u=q[l];
        For(u) if (!vis[o]){
            vis[o]=1;
            dis[o]=dis[u]+1;
            q[++r]=o;
        }
        dfs(val[u],dis[u]+1);       //討論一下 u 能連到的特殊路 
    }
    for (int i=1; i<=n; i++)
        printf("%d\n",dis[i]>n?-1:dis[i]);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章