排列
题目背景:
分析:并查集 + 贪心 + 堆(优先队列)
和之前雅礼集训中的送你一朵圣诞树几乎是一样的,只是多了一些伪装,考虑那个非常绕的条件究竟是什么意思,对于一个j <= k,ap[j]一定不能等于p[k],也就是说,对于一个i,ai在p中出现的位置编号要小于i在p中出现的位置编号。相当于就是ai是i的父亲,所有点的父亲要比自己先被选中,那么所有的1 ~ n都有且仅有一个前驱,一共n条边,如果不成环的话,就只能形成一个根为0的树,所以直接判断一下是不是这么一棵树,就可以判断无解了,然后之后的最大化答案,对于全局最小的那个点,选择了它的父亲之后,就一定会选择它,所以我们直接合并它和它的父亲,那么对于一个点集i,记录ti为点集中的点的个数,Si为点集中的点的权值和,那么,对于两个点集,如果点集i优于点集j,那么ti *sj > tj * si,那么si /ti < sj / tj,那么直接并查集 + 堆,每次合并当前块和父亲的块就可以了。
时间复杂度O(nlogn)
Source:
/* created by scarlyw */ #include <cstdio> #include <string> #include <algorithm> #include <cstring> #include <iostream> #include <cmath> #include <cctype> #include <vector> #include <set> #include <queue> #include <ctime> #include <bitset> inline char read() { static const int IN_LEN = 1024 * 1024; static char buf[IN_LEN], *s, *t; if (s == t) { t = (s = buf) + fread(buf, 1, IN_LEN, stdin); if (s == t) return -1; } return *s++; } ///* template<class T> inline void R(T &x) { static char c; static bool iosig; for (c = read(), iosig = false; !isdigit(c); c = read()) { if (c == -1) return ; if (c == '-') iosig = true; } for (x = 0; isdigit(c); c = read()) x = ((x << 2) + x << 1) + (c ^ '0'); if (iosig) x = -x; } //*/ const int OUT_LEN = 1024 * 1024; char obuf[OUT_LEN]; char *oh = obuf; inline void write_char(char c) { if (oh == obuf + OUT_LEN) fwrite(obuf, 1, OUT_LEN, stdout), oh = obuf; *oh++ = c; } template<class T> inline void W(T x) { static int buf[30], cnt; if (x == 0) write_char('0'); else { if (x < 0) write_char('-'), x = -x; for (cnt = 0; x; x /= 10) buf[++cnt] = x % 10 + 48; while (cnt) write_char(buf[cnt--]); } } inline void flush() { fwrite(obuf, 1, oh - obuf, stdout), oh = obuf; } /* template<class T> inline void R(T &x) { static char c; static bool iosig; for (c = getchar(), iosig = false; !isdigit(c); c = getchar()) if (c == '-') iosig = true; for (x = 0; isdigit(c); c = getchar()) x = ((x << 2) + x << 1) + (c ^ '0'); if (iosig) x = -x; } //*/ const int MAXN = 500000 + 10; int n; int a[MAXN], w[MAXN], father[MAXN], fa[MAXN], size[MAXN]; long long sum[MAXN]; std::vector<int> edge[MAXN]; bool vis[MAXN]; struct node { int id, size; long long val; inline node(int id, int size, long long val) : id(id), size(size), val(val) {} inline bool operator < (const node &a) const { return val * a.size > a.val * size; } } ; inline void add_edge(int x, int y) { edge[x].push_back(y); } inline void read_in() { R(n); for (int i = 1; i <= n; ++i) R(a[i]), add_edge(a[i], i); for (int i = 1; i <= n; ++i) R(w[i]); } inline void dfs(int cur, int fa) { father[cur] = fa, vis[cur] = true; for (int p = 0; p < edge[cur].size(); ++p) { int v = edge[cur][p]; if (v == fa) continue ; if (vis[v]) std::cout << "-1", exit(0); dfs(v, cur); } } inline int get_father(int x) { return (x == fa[x]) ? (x) : (fa[x] = get_father(fa[x])); } std::priority_queue<node> q; inline void solve() { dfs(0, 0); for (int i = 0; i <= n; ++i) if (vis[i] == false) std::cout << "-1", exit(0); long long ans = 0; for (int i = 1; i <= n; ++i) { fa[i] = i, size[i] = 1, sum[i] = w[i], ans += w[i]; q.push(node(i, 1, w[i])); } int cur; while (!q.empty()) { node temp = q.top(); q.pop(), cur = temp.id; if (get_father(cur) != cur || size[cur] != temp.size) continue ; int fa = get_father(father[cur]); ans += (long long)size[fa] * sum[cur]; size[fa] += size[cur], sum[fa] += sum[cur], ::fa[cur] = fa; if (fa != 0) q.push(node(fa, size[fa], sum[fa])); } std::cout << ans; } int main() { freopen("perm.in", "r", stdin); freopen("perm.out", "w", stdout); read_in(); solve(); return 0; }