HDU 5934 強連通+縮點 詳解
阿新 • • 發佈:2018-12-02
#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; }