NOI模拟(5.8) HNOID2T2 排列 (bzoj5289)

排列

题目背景:

5.8 模拟 HNOI2018D2T2

分析:并查集 + 贪心 + 堆(优先队列)

 

和之前雅礼集训中的送你一朵圣诞树几乎是一样的,只是多了一些伪装,考虑那个非常绕的条件究竟是什么意思,对于一个j <= kap[j]一定不能等于p[k],也就是说,对于一个iaip中出现的位置编号要小于ip中出现的位置编号。相当于就是aii的父亲,所有点的父亲要比自己先被选中,那么所有的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;
}

猜你喜欢

转载自blog.csdn.net/scar_lyw/article/details/80259068