1. 程式人生 > >2016CCPC杭州現場賽 B-Bomb /// tarjan縮點

2016CCPC杭州現場賽 B-Bomb /// tarjan縮點

題目大意:

給定n個爆破點的資訊 x y r w

表示爆破點位置為 (x,y) 爆破範圍是以位置為圓心 半徑為r的圓 引爆這個點的代價為w

引爆某個點時 其他位置在該爆破範圍內的爆破點也會被引爆

求引爆所有爆破點的最小的爆破代價

這道題跟 上一篇的 OJ 22833(POJ 2186) 差不多

那題是縮點再計算出度 這題是縮點再計算入度

 

以爆破關係建圖 即若引爆 i 點能使 j 點被引爆 那麼連一條 i 到 j 的邊

若存在點 k 的位置被包含在點 j 的爆破範圍內,點 j 的位置被包含在點 i 的爆破範圍內,但點 k 的位置不被包含在點 i 的爆破範圍內

此時若引爆 i 點 那麼 j 點會被引爆,而 j 點被引爆 k 點也會被引爆,即爆破存在傳遞性

 

所以求完這個圖的強聯通分量並縮點後 此時引爆這個圖的裡一個強聯通分量

那麼這個強聯通分量能到達的其他強聯通分量也會被引爆


又因為一個強聯通分量內任意兩點能互達 所以引爆一個強聯通分量只需要引爆它內部的其中一個點

那麼引爆一個強聯通分量 應該貪心地選擇它內部爆破代價最小的那個點

 

所以此時需要引爆的就是那些 入度為0的強聯通分量內爆破代價最小的點 (由它們開始發生連環引爆)(圖內的獨立點入度也為0)

 

#include <stdio.h>
#include <cstring>
#include <algorithm>
#include 
<stack> #define LL long long #define INF 0x3f3f3f3f using namespace std; const int N=1005; struct EDGE { int to, nt; }e[N*N]; int head[N], tot; int dfn[N], low[N], ind; int col[N], id; bool vis[N]; stack <int> s; int n, m, du[N]; LL x[N], y[N], r[N]; LL w[N], val[N]; void init() { while
(!s.empty()) s.pop(); for(int i=0;i<=n;i++) { head[i]=dfn[i]=low[i]=col[i]=-1; vis[i]=du[i]=0; val[i]=INF; } tot=ind=id=0; } void addE(int u,int v) { e[tot].to=v; e[tot].nt=head[u]; head[u]=tot++; } void tarjan(int u) { dfn[u]=low[u]=ind++; s.push(u); vis[u]=1; for(int i=head[u];i!=-1;i=e[i].nt) { int v=e[i].to; if(dfn[v]==-1) { tarjan(v); low[u]=min(low[u],low[v]); } else if(vis[v]) low[u]=min(low[u],low[v]); } if(dfn[u]==low[u]) { col[u]=++id; vis[u]=0; while(s.top()!=u) { col[s.top()]=id; vis[s.top()]=0; s.pop(); } s.pop(); } } bool uni(int i,int j) { LL dis=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]); if(dis<=r[i]*r[i]) return 1; return 0; } int main() { int t, tcase=0; scanf("%d",&t); while(t--) { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%lld%lld%lld%lld",&x[i],&y[i],&r[i],&w[i]); init(); for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) { if(uni(i,j)) addE(i,j); // j在i的爆破範圍內 連i->j if(uni(j,i)) addE(j,i); // i在j的爆破範圍內 連j->i } for(int i=1;i<=n;i++) if(dfn[i]==-1) tarjan(i); for(int i=1;i<=n;i++) { val[col[i]]=min(val[col[i]],w[i]); // 顏色相同說明在同個強聯通分量內 // 儲存這個強聯通分量內爆破代價最小的點 for(int j=head[i];j!=-1;j=e[j].nt) if(col[e[j].to]!=col[i]) du[col[e[j].to]]++; // 計算各個強聯通分量的入度 } LL ans=0LL; for(int i=1;i<=id;i++) if(du[i]==0) ans+=val[i]; // 入度為0 引爆 printf("Case #%d: %lld\n",++tcase,ans); } return 0; }
View Code