展翅翱翔之时 (はばたきのとき)
容易看出考察基环树(或者说“环套树”)的熟练运用。
变量解释:
变量名称 | 解释 |
---|---|
\(c_i\) | 第 \(i\) 个卫星调整接收源的所需花费 |
\(in_i\) | 指向点 \(i\) 的边的编号 |
\(out_i\) | 从点 \(i\) 延出的边的编号 |
\(vis_i\) | \(vis_i = 0\) 时未访问;\(vis_i = 1\) 时已访问但没在环上;\(vis_i = 2\) 时为在环上的点 |
\(fa_i\) | 原本指向点 \(i\) 的点的编号 |
\(loop_i\) | 环上的点 |
题目大意
给出一个有向图,求把这个有向图变成一个环的最小代价。
思路分析
对于一个基环树森林,我们将其分为几个独立的基环树,每一棵基环树把权值最大的取除,所有操作全部完成之后,再把已经不完整的基环树们连成一个环即可。
特别地,找到一棵基环树之后,如果一棵基环树的环的点数正好有 \(N\) 个,那么它已经满足条件是一棵基环树了,直接输出 \(0\) 即可,否则 #\(4\) 和 #\(7\) 会出错。
整个程序分为主函数、深度优先遍历和找环。
找环
选定一个需要深入的点 \(u\)。
-
如果点 \(u\) 未被访问,则显示访问此节点,并且依次访问指向此节点的点。
-
如果点 \(u\) 已被访问,那么将其放入环中,并且标记为环中的节点,并且依次访问指向此节点的点。
这是一个找环的过程,目的是找出环为接下来的 dfs 做准备。
dfs
对于一个节点 \(u\),访问其出边所连接的点,对其进行遍历。
这个步骤是为了找出价值最大的那条边,然后割掉它。
inline void dfs(int u,int fath)
{
if(vis[u]==0)
{
vis[u]=1;
}
for(register int i=head[u];i;i=node[i].nxt)
{
int v=node[i].v;
if(v!=fath && vis[v]!=2)
{
dfs(v,u);
if(c[out[u]]>c[i])
{
sum+=c[i];
}
else
{
sum+=c[out[u]];
out[u]=i;
}
}
}
}
完整代码:
//2021/8/4
#include <cstdio>
#define int long long
namespace sol
{
inline int min(int x,int y)
{
return x<y?x:y;
}
}
using namespace sol;
using namespace std;
const int INF=(1ll<<50);
const int ma=100005;
struct Node
{
int v;
int nxt;
};
Node node[ma<<1];
int head[ma],in[ma],out[ma],vis[ma],fa[ma],loop[ma],c[ma];
int n;
int idx,sum;
inline void add(int u,int v,int w)
{
node[++idx].v=v;
c[idx]=w;
node[idx].nxt=head[u];
head[u]=idx;
}
inline void dfs(int u,int fath)
{
if(vis[u]==0)
{
vis[u]=1;
}
for(register int i=head[u];i;i=node[i].nxt)
{
int v=node[i].v;
if(v!=fath && vis[v]!=2)
{
dfs(v,u);
if(c[out[u]]>c[i])
{
sum+=c[i];
}
else
{
sum+=c[out[u]];
out[u]=i;
}
}
}
}
inline void getloop(int u)
{
idx=0;
while(vis[u]==0)
{
vis[u]=1;
u=fa[u];
}
while(vis[u]==1)
{
loop[++idx]=u;
vis[u]=2;
u=fa[u];
}
}
#undef int
int main(void)
{
#define int long long
scanf("%lld",&n);
for(register int i=1;i<=n;i++)
{
int val;
scanf("%lld%lld",&fa[i],&val);
in[i]=i;
add(fa[i],i,val);
}
for(register int i=1;i<=n;i++)
{
if(vis[i]==0)
{
getloop(i);
if(idx==n)
{
printf("0\n");
return 0;
}
int ans1=0,ans2=INF;
for(register int j=1;j<=idx;j++)
{
dfs(loop[j],0);
}
for(register int j=1;j<=idx;j++)
{
ans2=min(min(ans1,ans2)+c[in[loop[j]]],ans2+c[out[fa[loop[j]]]]);
ans1+=c[out[fa[loop[j]]]];
}
sum+=ans2;
}
}
printf("%lld\n",sum);
return 0;
}