2021.08.09 P6037 Ryoku的探索(基环树)
P6037 Ryoku 的探索 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
重点:
1.树的性质
2.基环树的性质
题意:
Ryoku 所处的世界可以抽象成一个有 nn 个点, nn 条边的带权无向连通图 GG。每条边有美观度和长度。
Ryoku 会使用这样一个策略探索世界:在每个点寻找一个端点她未走过的边中美观度最高的走,如果没有边走,就沿着她前往这个点的边返回,类似于图的深度优先遍历。
探索的一个方案的长度是这个方案所经过的所有边长度的和(返回时经过的长度不用计算)。
她想知道,对于每一个起点 s=1,2,⋯,n,她需要走过的长度是多少?
分析及代码:
//这是一棵美丽的基环树。
//先思考只是一棵树的情况:遍历所有边
//如果是一棵有x个点在环上的基环树呢?
//先从根结点出发,肯定要遍历子树
//接着访问环上左右两个边中最美丽的边,一路走到另一边的端点
//不从根节点出发,那就先遍历子树,走到根节点~
#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long ll;
const int N=1e6+10;
int n,vis[N],cnt,head[N],ind,dfn[N],root[N],ans[N],fa[N];
ll sum;
struct node{
int to,next,val,beauty;
}a[N<<1];
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')w=-1;
ch=getchar();
}
while(ch<='9'&&ch>='0'){
s=s*10+ch-'0';
ch=getchar();
}
return s*w;
}
inline void add(int u,int v,int w,int x){
++cnt;
a[cnt].to=v;
a[cnt].val=w;
a[cnt].beauty=x;
a[cnt].next=head[u];
head[u]=cnt;
}
inline void find(int x){
++ind;
dfn[x]=ind;
for(int i=head[x];i;i=a[i].next){
int v=a[i].to;
if(v==fa[x])continue;
if(dfn[v]){
if(dfn[v]<dfn[x])continue;
vis[v]=1;
while(v!=x)vis[fa[v]]=1,v=fa[v];
}else{
fa[v]=x;
find(v);
}
}
}
void dfs(int x,int rt){
fa[x]=rt;
root[x]=1;
for(int i=head[x];i;i=a[i].next){
int v=a[i].to;
if(root[v]||vis[v])continue;
dfs(v,rt);
}
}
int main(){
n=read();
for(int i=1;i<=n;i++){
int u,v,w,x;
u=read();v=read();w=read();x=read();
add(u,v,w,x);add(v,u,w,x);
sum+=w;
}
find(1);
for(int i=1;i<=n;i++){
if(vis[i]){
dfs(i,i);
int minn=0x3f3f3f3f;
for(int j=head[i];j;j=a[j].next){
int v=a[j].to;
if(vis[v]&&a[j].beauty<minn)ans[i]=a[j].val,minn=a[j].beauty;
}
}
}
for(int i=1;i<=n;i++)cout<<sum-1ll*ans[fa[i]]<<endl;
return 0;
}