「AHOI / HNOI2018」排列(DAG上贪心+堆)

https://loj.ac/problem/2509

\(a[i]->i\)连边,图不是DAG无解,之后就是\(p\)必须是一个DAG的Turpo序,然后使得\(\sum i*w[p[i]]\)最大。

考虑无论是正着贪心还是反着贪心,都TM有后效性,然后这个题不太能可撤销贪心。

之后的套路好像在哪里见过类似的,但忘了是在哪里。

考虑最小的\(w[i]\),如果它的父亲选了,就一定会马上选它,那么我们可以将它们合并成一段数。

然后继续选最小的,对于两段数,设和分别为\(p1,p2\),。个数分别为\(q1,q2\),发现\(p1、q1\)在前面更优的条件是\(p1*q2<p2*q1->\frac{p1}{q1}<\frac{p2}{q2}\)

那么每一段数的新权值可以设为平均数,需要支持删除和插入和找最小值的数据结构,堆即可。

还需要用个并查集去合并数。

Code:

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

const int N = 5e5 + 5;

int n, fa[N], w[N];
int f[N];

int F(int x) { return f[x] == x ? x : (f[x] = F(f[x]));}

ll p[N], q[N], s[N];

int bz[N];

struct P {
	ll p, q, s; int i;	
};

bool operator < (P a, P b) {
	if(a.p * b.q != b.p * a.q) return a.p * b.q < b.p * a.q;
	return a.i < b.i;	
}

multiset<P> t;

ll ans, ans2;

int main() {
	scanf("%d", &n);
	fo(i, 1, n) scanf("%d", &fa[i]);
	fo(i, 1, n) scanf("%d", &w[i]);
	fo(i, 1, n) {
		f[i] = i;
		p[i] = w[i], q[i] = 1, s[i] = w[i];
		t.insert((P) {p[i], q[i], s[i], i});
	}
	fo(i, 1, n) {
		P b = *t.begin(); t.erase(t.begin());
		if(fa[b.i] == 0) {
			bz[b.i] = 1;
			ans += b.p * ans2 + b.s;
			ans2 += b.q;
		} else {
			int x = F(b.i), y = F(fa[b.i]);
			if(x == y) {
				pp("-1\n"); return 0;
			}
			f[x] = y;
			if(bz[y]) {
				ans += b.p * ans2 + b.s;
				ans2 += b.q;
				continue;
			}
			t.erase(t.find((P) {p[y], q[y], s[y], y}));
			s[y] += s[x] + p[x] * q[y];
			p[y] += p[x], q[y] += q[x];
			t.insert((P) {p[y], q[y], s[y], y});
		}
	}
	pp("%lld\n", ans);
}
posted @ 2020-04-13 22:29  Cold_Chair  阅读(177)  评论(0编辑  收藏  举报