1. 程式人生 > >HDU 5934 強連通+縮點 詳解

HDU 5934 強連通+縮點 詳解

#include<bits/stdc++.h>
typedef long long int LL;
using namespace std;

const int N   = 2333+7;
const int INF = (~(1<<31))>>1;
int x[N],y[N],r[N],c[N];

int dfn[N],low[N],color[N];
int vis[N],cnt,tot;//(1為在棧中,2為已訪問,且不在棧中,0為不在,沒訪問過)
int cost[N],deg[N];
int mystack[N],len;
vector<int>G[N];

void dfs(int u){
    dfn[u]=low[u]=++cnt;//時間點
    vis[u]=1;//標記
    mystack[++len]=u;//入棧
    int gz=G[u].size();
    for(int i=0,to;i<gz;i++){
        to=G[u][i];
        if(vis[to]==0)dfs(to);//沒訪問過
        if(vis[to]==1)low[u]=min(low[u],low[to]);//與子樹更新最小值
    }
    if(dfn[u]==low[u]){//聯通塊
        ++tot;int j;
        do{

            j=mystack[len--]; 
            cost[tot]=min(cost[tot],c[j]);//最小花費
            color[j]=tot;//聯通塊的編號 縮點
            vis[j]=2;//標記 已訪問
        }while(j!=u);//回溯
    }
}

LL dis(LL x){return x*x;}

bool judge(int i,int j){//i與j之間的據離與i的半徑比較
    return dis(x[i]-x[j])+dis(y[i]-y[j])<=dis(r[i]);
}

int n;
int main(){
    int _,kcase=0;scanf("%d",&_);
    while(_--){
        tot=cnt=len=0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d%d%d%d",&x[i],&y[i],&r[i],&c[i]);

        for(int i=1;i<=n;i++){cost[i]=INF,deg[i]=vis[i]=0;
            for(int j=1;j<=n;j++){
                if(i==j) continue;//構圖
                if(judge(i,j))G[i].push_back(j);
            }
        }
        for(int i=1;i<=n;i++) if(vis[i]==0) dfs(i);

        for(int i=1;i<=n;i++){//染色
            int gz=G[i].size();
            for(int j=0,to;j<gz;j++){
                to=G[i][j];
                if(color[i]!=color[to])//編號不相等 不在同一個環
                    deg[color[to]]++;//i-->to to頂點入度++
            }
        }
        int ans=0;
        for(int i=1;i<=tot;i++)//聯通快 
            {if(deg[i]==0) ans+=cost[i];//入度為零 需引爆
        printf("Case #%d: %d\n",++kcase,ans);
        for(int i=1;i<=n;i++) G[i].clear();
    }
    return 0;
}