There are
N
N bombs needing exploding.
Each bomb has three attributes: exploding radius ri ri, position (xi,yi) (xi,yi) and lighting-cost ci ci which means you need to pay ci ci cost making it explode.
If a un-lighting bomb is in or on the border the exploding area of another exploding one, the un-lighting bomb also will explode.
Now you know the attributes of all bombs, please use the minimum cost to explode all bombs.
Each bomb has three attributes: exploding radius ri ri, position (xi,yi) (xi,yi) and lighting-cost ci ci which means you need to pay ci ci cost making it explode.
If a un-lighting bomb is in or on the border the exploding area of another exploding one, the un-lighting bomb also will explode.
Now you know the attributes of all bombs, please use the minimum cost to explode all bombs.
Every test case begins with an integers N N, which indicates the numbers of bombs.
In the following N N lines, the ith line contains four intergers xi xi, yi yi, ri ri and ci ci, indicating the coordinate of ith bomb is (xi,yi) (xi,yi), exploding radius is ri ri and lighting-cost is ci ci.
Limits
- 1≤T≤20 1≤T≤20
- 1≤N≤1000 1≤N≤1000
- −108≤xi,yi,ri≤108 −108≤xi,yi,ri≤108
- 1≤ci≤104 1≤ci≤104
1 5 0 0 1 5 1 1 1 6 0 1 1 7 3 0 2 10 5 0 1 4
Case #1: 15
【分析】:
根据包含关系建立边,若圆a能包含圆b的圆心,则建边a->b。
建图后,找出强连通分量,一个分量里只要选中一个点,其他点都会接连爆炸。注意还有可能通过一条边引爆其他的连通分量。
tarjan算法找连通分量,缩点,记录分量里权值最小的点。
【代码】:
#include<bits/stdc++.h>
#define mset(a,i) memset(a,i,sizeof(a))
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int MAX=2000;
inline ll SI(){ll x;scanf("%lld",&x);return x;}
struct point{
ll x,y,r,c;
}p[1010];
struct node{
int s,t,next;
}e[MAX*MAX];
int head[MAX],cnt;
void add(int u,int v)
{
e[cnt]=node{u,v,head[u]};
head[u]=cnt++;
}
ll mins[MAX];
int dfn[MAX];//每个节点的访问时间编号
int low[MAX];//每个点能到达的最小编号
int sta[MAX],top;
int Scc[MAX];//每个点所属的分量 序号
void tardfs(int u,int &lay,int &sig)
{
low[u]=dfn[u]=lay++;//到达此点的时间
sta[top++]=u;//压入栈
for(int i=head[u];~i;i=e[i].next)
{
int t=e[i].t;
if(dfn[t]==0)
{
tardfs(t,lay,sig);
low[u]=min(low[u],low[t]);
}
else if(!Scc[t])//访问过了
low[u]=min(low[u],dfn[t]);
}
if(low[u]==dfn[u])//u不能到达任何之前走过的点
{
sig++;
while(1)
{
int j=sta[--top];
Scc[j]=sig;
mins[sig]=min(mins[sig],p[j].c);
if(j==u)break;
}//包含u的连通分量出栈
}
}
int tarjan(int n)
{
int sig=0;
int lay=1;//香蕉戳
top=0;
memset(Scc,0,sizeof(Scc));
memset(dfn,0,sizeof(dfn));//香蕉戳归0
for(int i=1;i<=n;i++)
{
if(!dfn[i])tardfs(i,lay,sig);
}
return sig;//返回连通数
}
bool check(point a,point b)
{
ll len2=(a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
if(a.r*a.r>=len2)return 1;
return 0;
}
int main()
{
int n,T=SI();
for(int h=1;h<=T;h++)
{
mset(head,-1);
cnt=0;
n=SI();
for(int i=1;i<=n;i++)
{
p[i].x=SI();
p[i].y=SI();
p[i].r=SI();
p[i].c=SI();
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i!=j&&check(p[i],p[j]))
add(i,j);
}
}
for(int i=0;i<=n;i++)mins[i]=INF;
int sig=tarjan(n);
ll ans=0;
for(int i=0;i<cnt;i++)
{
int u=e[i].s;
int v=e[i].t;
if(Scc[u]!=Scc[v])
{
mins[Scc[v]]=0;
}
}
for(int i=sig;i>=1;i--)
{
ans+=mins[i];
}
printf("Case #%d: ",h);
cout<<ans<<endl;
}
return 0;
}