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 }

 

posted @ 2020-10-07 23:39  古比  阅读(131)  评论(0编辑  收藏  举报