B - Bomb CCPC tarjan
题目大意:
给定n个爆破点的信息 x y r w
表示爆破点位置为 (x,y) 爆破范围是以位置为圆心 半径为r的圆 引爆这个点的代价为w
引爆某个点时 其他位置在该爆破范围内的爆破点也会被引爆
求引爆所有爆破点的最小的爆破代价
以爆破关系建图 即若引爆 i 点能使 j 点被引爆 那么连一条 i 到 j 的边
若存在点 k 的位置被包含在点 j 的爆破范围内,点 j 的位置被包含在点 i 的爆破范围内,但点 k 的位置不被包含在点 i 的爆破范围内
此时若引爆 i 点 那么 j 点会被引爆,而 j 点被引爆 k 点也会被引爆,即爆破存在传递性
所以求完这个图的强联通分量并缩点后 此时引爆这个图的里一个强联通分量
那么这个强联通分量能到达的其他强联通分量也会被引爆
又因为一个强联通分量内任意两点能互达 所以引爆一个强联通分量只需要引爆它内部的其中一个点
那么引爆一个强联通分量 应该贪心地选择它内部爆破代价最小的那个点
所以此时需要引爆的就是那些 入度为0的强联通分量内爆破代价最小的点 (由它们开始发生连环引爆)(图内的独立点入度也为0)
1 #include <stdio.h> 2 #include <cstring> 3 #include <algorithm> 4 #include <stack> 5 #define LL long long 6 #define INF 0x3f3f3f3f 7 using namespace std; 8 9 const int N=1005; 10 struct EDGE { int to, nt; }e[N*N]; 11 int head[N], tot; 12 int dfn[N], low[N], ind; 13 int col[N], id; 14 bool vis[N]; 15 stack <int> s; 16 17 int n, m, du[N]; 18 LL x[N], y[N], r[N]; 19 LL w[N], val[N]; 20 21 void init() { 22 while(!s.empty()) s.pop(); 23 for(int i=0;i<=n;i++) { 24 head[i]=dfn[i]=low[i]=col[i]=-1; 25 vis[i]=du[i]=0; val[i]=INF; 26 } 27 tot=ind=id=0; 28 } 29 void addE(int u,int v) { 30 e[tot].to=v; 31 e[tot].nt=head[u]; 32 head[u]=tot++; 33 } 34 35 void tarjan(int u) { 36 dfn[u]=low[u]=ind++; 37 s.push(u); vis[u]=1; 38 for(int i=head[u];i!=-1;i=e[i].nt) { 39 int v=e[i].to; 40 if(dfn[v]==-1) { 41 tarjan(v); 42 low[u]=min(low[u],low[v]); 43 } 44 else if(vis[v]) 45 low[u]=min(low[u],low[v]); 46 } 47 if(dfn[u]==low[u]) { 48 col[u]=++id; 49 vis[u]=0; 50 while(s.top()!=u) { 51 col[s.top()]=id; 52 vis[s.top()]=0; 53 s.pop(); 54 } s.pop(); 55 } 56 } 57 58 bool uni(int i,int j) { 59 LL dis=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]); 60 if(dis<=r[i]*r[i]) return 1; 61 return 0; 62 } 63 64 int main() 65 { 66 int t, tcase=0; 67 scanf("%d",&t); 68 while(t--) { 69 scanf("%d",&n); 70 for(int i=1;i<=n;i++) 71 scanf("%lld%lld%lld%lld",&x[i],&y[i],&r[i],&w[i]); 72 init(); 73 for(int i=1;i<=n;i++) 74 for(int j=i+1;j<=n;j++) { 75 if(uni(i,j)) addE(i,j); // j在i的爆破范围内 连i->j 76 if(uni(j,i)) addE(j,i); // i在j的爆破范围内 连j->i 77 } 78 79 for(int i=1;i<=n;i++) 80 if(dfn[i]==-1) tarjan(i); 81 82 for(int i=1;i<=n;i++) { 83 val[col[i]]=min(val[col[i]],w[i]); 84 // 颜色相同说明在同个强联通分量内 85 // 保存这个强联通分量内爆破代价最小的点 86 for(int j=head[i];j!=-1;j=e[j].nt) 87 if(col[e[j].to]!=col[i]) 88 du[col[e[j].to]]++; // 计算各个强联通分量的入度 89 } 90 91 LL ans=0LL; 92 for(int i=1;i<=id;i++) 93 if(du[i]==0) ans+=val[i]; // 入度为0 引爆 94 printf("Case #%d: %lld\n",++tcase,ans); 95 } 96 97 return 0; 98 }