USACO22OPEN-Silver B


USACO22OPEN-Silver B

前言(Front talk)

$\qquad$有趣的题,竟然可以用最小生成树来做,拓扑排序判环也可以。

网址(Website)

$\qquad$洛谷: USACO22OPEN Visits S

$\qquad$SPOJ: 题目详情 - Visits - Super

$\qquad$SPOJ: 题解 - Visits - Super

题目(Problem)

$\qquad$Bessie 的 $N$($2\le N\le 10^5$)个奶牛伙伴(编号为 $1\cdots N$)每一个都拥有自己的农场。对于每个 $1\le i\le N$,伙伴 i 想要访问伙伴 $a_i$($a_i\neq i$)。

$\qquad$给定 $1\ldots N$ 的一个排列 $(p_1,p_2,\ldots, p_N)$,访问按以下方式发生。

$\qquad$对于 $1$ 到 $N$ 的每一个 $i$:

  • 如果伙伴 $a_{p_i}$ 已经离开了她的农场,则伙伴 $p_i$ 仍然留在她的农场。
  • 否则,伙伴 $p_i$ 离开她的农场去访问伙伴 $a_{p_i}$ 的农场。这次访问会产生快乐的哞叫 $v_{p_i}$ 次($0\le v_{p_i}\le 10^9$)。

$\qquad$对于所有可能的排列 $p$,计算所有访问结束后可能得到的最大哞叫次数。

输入格式(Input)

$\qquad$输入的第一行包含 $N$。

$\qquad$对于每一个 $1\le i\le N$,第 $i+1$ 行包含两个空格分隔的整数 $a_i$ 和 $v_i$。

输出格式(Output)

$\qquad$输出一个整数,为所求的答案。

$\qquad$注意这个问题涉及到的整数可能需要使用 64 位整数型(例如,C/C++ 中的 “long long”)。

样例(Example)

样例输入

4
2 10
3 20
4 30
1 40

样例输出

90

提示(Notice)

样例解释

如果 $p=(1,4,3,2)$,则

  • 伙伴 $1$ 访问伙伴 $2$ 的农场,产生 $10$ 次哞叫。
  • 伙伴 $4$ 看到伙伴 $1$ 已经离开了农场,所以无事发生。
  • 伙伴 $3$ 访问伙伴 $4$ 的农场,又产生 $30$ 次哞叫。
  • 伙伴 $2$ 看到伙伴 $3$ 已经离开了农场,所以无事发生。

这样总计得到了 $10+30=40$ 次哞叫。

另一方面,如果 $p=(2,3,4,1)$,则

  • 伙伴 $2$ 访问伙伴 $3$ 的农场,产生 $20$ 次哞叫。
  • 伙伴 $3$ 访问伙伴 $4$ 的农场,产生 $30$ 次哞叫。
  • 伙伴 $4$ 访问伙伴 $1$ 的农场,产生 $40$ 次哞叫。
  • 伙伴 $1$ 看到伙伴 $2$ 已经离开了农场,所以无事发生。

这样总计得到了 $20+30+40=90$ 次哞叫。可以证明这是所有可能的排列 $p$ 中访问结束后得到的最大可能的哞叫次数。

题解(Solution)

$\qquad$以当前农场为 $x$ ,以要去的农场为 $y$ ,以开心值 $v$ 建边。

$\qquad$原图构成了基环树森林,由于环内没有出边,很容易便分析出所有的指向环的点都是可以直接得到的,对于环内,需要去掉一条边权最小的点,于是乎,可以拓扑排序求环,对于每个环遍历一次最小边,去掉贡献即可。

代码(Code)

我的代码(拓扑排序)

#include<bits/stdc++.h>
#include<vector>
#include<queue>
#define For(i,l,r) for(int i=l;i<=r;i++)
using namespace std;
#define P pair<int,int>
#define int long long 
inline int input(){int x;return cin>>x,x;}
const int inf = 0x3f3f3f3f3f3f3f3f;
const int N = 101234;

int n,ans,inn[N];
vector<P> ft[N];
bitset<N> vst;

inline int dfs(int x){
	vst[x]=true;
	int minn=inf;for(auto p:ft[x]){
		int y=p.first,v=p.second;
		minn=min(minn,v);
//		printf("%d %d %d min=%d\n",x,y,v,minn);
		if(vst[y]) continue;
		minn=min(dfs(y),minn);
	}return minn;
}

queue<int> q;
void Tuopu(){
	For(x,1,n) if(!inn[x]) q.push(x);
	while(q.size()){
		int x=q.front();q.pop();
		vst[x]=true;
		for(auto p:ft[x]){
			int y=p.first;
			if(--inn[y]==0ll) q.push(y);
		}
	}For(x,1,n) if(!vst[x]) ans-=dfs(x);
	return;
}

signed main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	string str("visits");
	freopen((str+".in").c_str(),"r",stdin);
	freopen((str+".out").c_str(),"w",stdout);
	cin>>n;For(x,1,n){
		int y=input(),v=input();
		ft[x].push_back({y,v});
		inn[y]++;ans+=v;
	}Tuopu();
	cout<<ans<<'\n';
	return 0;
}

cx的代码(最小生成树)

#include <bits/stdc++.h>
using namespace std;
#define N 100010
using ll = long long;
int a[N];
ll v[N];
int n;
int fa[N];
int getfa(int v) {
  return v == fa[v] ? v : fa[v] = getfa(fa[v]);
}
int p[N];
ll ans;
int main() {
  freopen("visits.in", "r", stdin);
  freopen("visits.out", "w", stdout);
  scanf("%d", &n);
  for (int i = 1; i <= n; ++i) fa[i] = i;
  for (int i = 1; i <= n; ++i) {
    scanf("%d%lld", a + i, v + i);
    p[i] = i;
  }
  sort(p + 1, p + n + 1, [&](int x, int y){return v[x] > v[y];});
  for (int i = 1; i <= n; ++i) {
    int x = getfa(p[i]), y = getfa(a[p[i]]);
    if (x != y) {
      ans += v[p[i]];
      fa[x] = y;
    }
  }
  printf("%lld\n", ans);
  return 0;
}

文章作者: WolfDeer
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 WolfDeer !
  目录