2417. 指挥网络
题目链接
2417. 指挥网络
在漫长的骂战过后,利特肯王国和克努斯海洋王国之间爆发了一场武装战争。
克努斯海洋王国部队的猛烈进攻使得利特肯王国的指挥网络彻底瘫痪。
临时指挥网络的建立刻不容缓。
利特肯命令史努比负责该项目。
利特肯王国共有 \(N\) 个指挥部,位于平面中的 \(N\) 个节点上(编号 \(1 \sim N\))。
其中利特肯所在的指挥总部位于节点 \(1\)。
通过对战时情况的详尽研究,史努比认为,当前最关键的一点在于建立一个单向通信网络,使得利特肯的命令能够成功传达至平面中的每个节点处。
如果希望利特肯的命令能够直接从节点 \(A\) 传递到另一个节点 \(B\),则必须沿着连接两个节点的直线段构建一条单向传输电线。
因为战争还未停止,所以并不是所有节点对之间都能建立电线。(甚至能够建立从节点 \(A\) 传递消息至节点 \(B\) 的电线,也不一定能够建立从节点 \(B\) 传递消息至节点 \(A\) 的电线)
史努比希望这项工程所需耗费的电线长度尽可能短,以便施工可以尽快完成。
输入格式
输入包含若干测试数据。
每组数据第一行包含两个整数 \(N,M\),表示节点总数以及可在其间建立电线的节点对数。
接下来 \(N\) 行,其中第 \(i\) 行包含两个整数 \(x_i,y_i\),表示节点 \(i\) 的位置坐标为 \((x_i,y_i)\)。
接下来 \(M\) 行,每行包含两个整数 \(a,b\),表示可以建立一条单向电线使得命令可以从节点 \(a\) 传递至节点 \(b\)。
处理至文件末尾。
输出格式
对于每个测试数据,输出结果占一行。
如果临时网络可以成功构建,则输出所需耗费电线的最小可能长度,保留两位小数。
如果不能成功构建,则输出 poor snoopy
。
数据范围
\(1 \le N \le 100\),
\(1 \le M \le 10^4\),
\(0 \le x_i,y_i \le 10^5\),
\(1 \le a,b \le N\),\(a \neq b\),
每个输入最多包含 \(10\) 组测试。
输入样例:
4 6
0 6
4 6
0 0
7 20
1 2
1 3
2 3
3 4
3 1
3 2
4 3
0 0
1 0
0 1
1 2
1 3
4 1
2 3
输出样例:
31.19
poor snoopy
解题思路
朱刘算法,最小树形图
树形图:对于有向图而言,其无环,且除根外的所有点的入度都为 \(1\)
朱刘算法主要用来求解有向图的最小树形图,即总权值最小的树形图
算法流程:对于每个点都找一条权值最小的指向该点的边,如果这样的边不能构成一个环的话,则说明找到答案,结束整个算法流程,否则缩点,对于某条指向该缩点的边,其权值需要再减去其指向缩点表示的环的点的前驱边的权值,然后继续找出这样的边,直到没有环出现
证明略
- 时间复杂度:\(O(nm)\)
代码
// Problem: 指挥网络
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/2419/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
typedef pair<double,double> PDD;
const int N=105;
const double inf=1e9;
int n,m;
bool g[N][N],st[N];
PDD a[N];
double d[N][N],bd[N][N];
int pre[N];
int dfn[N],low[N],scc_cnt,id[N],timestamp,stk[N],top;
bool in_stk[N];
void dfs(int x)
{
st[x]=true;
for(int i=1;i<=n;i++)
if(g[x][i]&&!st[i])dfs(i);
}
bool check_con()
{
memset(st,0,sizeof st);
dfs(1);
for(int i=1;i<=n;i++)
if(!st[i])return false;
return true;
}
double get_dist(int x,int y)
{
double dx=a[x].fi-a[y].fi;
double dy=a[x].se-a[y].se;
return sqrt(dx*dx+dy*dy);
}
void tarjan(int x)
{
dfn[x]=low[x]=++timestamp;
stk[++top]=x,in_stk[x]=true;
int y=pre[x];
if(!dfn[y])
{
tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(in_stk[y])low[x]=min(low[x],dfn[y]);
if(low[x]==dfn[x])
{
int y;
scc_cnt++;
do
{
y=stk[top--];
in_stk[y]=false;
id[y]=scc_cnt;
}while(y!=x);
}
}
double work()
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(g[i][j])d[i][j]=get_dist(i,j);
else
d[i][j]=inf;
double res=0;
while(true)
{
for(int i=1;i<=n;i++)
{
pre[i]=i;
for(int j=1;j<=n;j++)
if(d[pre[i]][i]>d[j][i])
pre[i]=j;
}
memset(dfn,0,sizeof dfn);
scc_cnt=timestamp=top=0;
for(int i=1;i<=n;i++)
if(!dfn[i])tarjan(i);
if(scc_cnt==n)
{
for(int i=2;i<=n;i++)res+=d[pre[i]][i];
break;
}
for(int i=2;i<=n;i++)
if(id[i]==id[pre[i]])res+=d[pre[i]][i];
for(int i=1;i<=scc_cnt;i++)
for(int j=1;j<=scc_cnt;j++)
bd[i][j]=inf;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(d[i][j]<inf&&id[i]!=id[j])
{
int x=id[i],y=id[j];
if(id[pre[j]]==id[j])
bd[x][y]=min(bd[x][y],d[i][j]-d[pre[j]][j]);
else
bd[x][y]=min(bd[x][y],d[i][j]);
}
n=scc_cnt;
memcpy(d,bd,sizeof bd);
}
return res;
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
memset(g,0,sizeof g);
for(int i=1;i<=n;i++)scanf("%lf%lf",&a[i].fi,&a[i].se);
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
if(x!=y&&y!=1)g[x][y]=true;
}
if(!check_con())puts("poor snoopy");
else
printf("%.2lf\n",work());
}
return 0;
}