创世纪
题面
上帝手中有 N 种世界元素,每种元素可以限制另外1种元素,把第 i 种世界元素能够限制的那种世界元素记为 A[i]。
现在,上帝要把它们中的一部分投放到一个新的空间中去建造世界。
为了世界的和平与安宁,上帝希望所有被投放的世界元素都有至少一个没有被投放的世界元素限制它。
上帝希望知道,在此前提下,他最多可以投放多少种世界元素?
输入格式
第一行是一个整数N,表示世界元素的数目。
第二行有 N 个整数A[1], A[2], …, A[N]。A[i] 表示第 i 个世界元素能够限制的世界元素的编号。
输出格式
一个整数,表示最多可以投放的世界元素的数目。
数据范围
N≤106,1≤A[i]≤N
输入样例:
6
2 3 1 3 6 5
输出样例:
3
题解
明显是基环树森林,
对于一颗树, 就好做得多, 设 F[x, t(0, 1)] 表示节点x放与不放
\(F[x, 0] = \sum_{A[y] = x}max(F[y, 0], F[y, 1])\)
\(F[x, 1] = MAX_{A[y] = x} (F[y, 0] + \sum_{A[z] = x, z != y}max(F[z, 0], F[z, 1])\)
观察发现 \(F[x, 1] = 1 + F[x, 0] - MIN_{A[y] = x}(max(F[y, 0], F[y, 1]) - F[y, 0])\)
那对于一颗基环树, 无非是拆边, 然后算, 影响在于
不选择选择 拆环后的根节点 时, 对于 A[root] 已经有 root 最为约束而没被选择, 故 F[A[root], 1] = F[x, 0], 在递归的时候特判就行了
复杂度为O(N)
#include <bits/stdc++.h>
#define all(n) (n).begin(), (n).end()
#define se second
#define fi first
#define pb push_back
#define mp make_pair
#define sqr(n) (n)*(n)
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define per(i,a,b) for(int i=a;i>=(b);--i)
#define IO ios::sync_with_stdio(0); cin.tie(0);
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
typedef pair<ll, ll> PLL;
typedef vector<int> VI;
typedef double db;
const int N = 1e6 + 5;
int n, m, _, k, t;
int h[N], to[N], ne[N], tot;
int a[N], rt, f[N][2], ans;
bool v[N];
void add(int u, int v) {
ne[++tot] = h[u]; h[u] = tot; to[tot] = v;;
}
ll solve(int u, bool t) {
f[u][0] = 0; v[u] = 1;
int res, c = 1e9;
for (int i = h[u]; i; i = ne[i]) {
int y = to[i];
if (y == rt) continue;
res = solve(y, t);
c = min(c, res - f[y][0]);
f[u][0] += res;
}
f[u][1] = f[u][0] - c + 1;
if (t && u == a[rt]) f[u][1] += c;
return max(f[u][0], f[u][1]);
}
ll work(int u) {
rt = u;
while (!v[a[rt]]) v[rt] = 1, rt = a[rt];
int ans = solve(rt, 0);
solve(rt, 1);
return max(ans, f[rt][0]);
}
int main() {
IO; cin >> n;
rep(i, 1, n) cin >> a[i], add(a[i], i);
rep(i, 1, n)
if (!v[i]) ans += work(i);
cout << ans;
return 0;
}