[BZOJ5249][2018多省省队联测]IIIDX(贪心+线段树)

Address

洛谷P4364
BZOJ5249
LOJ#2472

Solution

被这道题虐了两天
由于 i k < i \lfloor\frac ik\rfloor<i ,所以难度的拓扑序是一棵树或森林。
具体地,我们把 i k \lfloor\frac ik\rfloor (如果存在)作为 i i 的父亲节点。
问题转化为将 d d 数组填充进节点的权值,使得每个点子树内的权值都不小于这个点的权值,并输出字典序最大的方案。
字典序显然可以贪心。
考虑记下子树大小 s i z e size ,并将 d d 从大到小排序。
一个优 (cuo) 秀 (wu) 的贪心:
节点 1 1 为根,那么节点 1 1 及其子树内分配到 d [ 1... s i z e [ 1 ] ] d[1...size[1]] 的权值。
如果 2 2 为根,那么 2 2 及其子树内分配到 d [ s i z e [ 1 ] + 1... s i z e [ 1 ] + s i z e [ 2 ] ] d[size[1]+1...size[1]+size[2]] 的权值。
如果 3 3 还是根,则其子树分配到 d [ s i z e [ 1 ] + s i z e [ 2 ] + 1... s i z e [ 1 ] + s i z e [ 2 ] + s i z e [ 3 ] ] d[size[1]+size[2]+1...size[1]+size[2]+size[3]] ,以此类推。
如果 u u 有父亲 v v v v 预分配到了 d [ l . . . r ] d[l...r] ,就同样把 d [ l . . . r ] d[l...r] 如上面一样分组并分配给 u u
才怪。 55 分。
考虑 d d 有相同权值的时候,如:
n = 6 , k = 3 , d = { 3 , 3 , 2 , 2 , 2 , 2 } n=6,k=3,d=\{3,3,2,2,2,2\}
那么这是由两棵树构成的森林,一棵大小为 4 4 另一棵为 2 2
显然,第一棵树要分配权值的最小值为 2 2 ,即节点 1 1 要填充 2 2
然而如果这时第一棵树不填充 { 3 , 3 , 2 , 2 } \{3,3,2,2\} 而填充 { 2 , 2 , 2 , 2 } \{2,2,2,2\} 就可以把剩下的 { 3 , 3 } \{3,3\} 让给第二棵树,得到字典序更大的解。
于是我们改变贪心策略:在 d d 从右往左找到最左位置 x x 使得节点 x x 的左边(包括 x x )至少存在 s i z e [ u ] size[u] 个未被取走的节点,然后找到一个 y y 使得满足 d [ x ] = d [ y ] d[x]=d[y] y y 最大。这时候节点 u u 填充的权值为 d [ y ] d[y]
考虑用线段树。设 p [ i ] p[i] 表示 d [ i ] d[i] 的左边还有多少个点可取。
计算一个节点 u u 时,可以得出如果 u u 的权值能取 d [ i ] d[i] ,那么一定满足:
min j = i n p [ j ] s i z e [ u ] \min_{j=i}^np[j]\ge size[u]
可以在线段树上二分得到这个最小的 i i 。需要记录区间最小值。
然后找到一个 j j 满足 d [ i ] = d [ j ] d[i]=d[j] d [ j ] d[j] 没有被取走并且 j j 最大。
然后把 u u 的权值设为 d [ j ] d[j] ,这时候, p [ j . . . n ] p[j...n] 都要减去 s i z e [ u ] size[u] 。维护标记即可。这可以看成是为 u u 的子树预留 s i z e [ u ] size[u] 个权值。
同时注意细节:如果一个点 u u 有父亲 v v ,那么这时候 u u 就可 (bi) 以 (xu) 放在 v v 的子树内(废话),所以要把 v v 为子树所预留的点(不包括 v v 自己)还给 u u 以及 v v 的其他子节点,也就是 p [ a n s [ v ] . . . n ] p[ans[v]...n] 减去 s i z e [ v ] 1 size[v]-1 。特别注意 v v 只需要在第一个子节点 u u 处消除影响一次。

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
#define p2 p << 1
#define p3 p << 1 | 1
using namespace std;
inline int read() {
	int res = 0; bool bo = 0; char c;
	while (((c = getchar()) < '0' || c > '9') && c != '-');
	if (c == '-') bo = 1; else res = c - 48;
	while ((c = getchar()) >= '0' && c <= '9')
		res = (res << 3) + (res << 1) + (c - 48);
	return bo ? ~res + 1 : res;
}
const int N = 5e5 + 5, M = N << 2;
int n, d[N], fa[N], sze[N], f[M], add[M], pre[N], nxt[N], ans[N];
double k;
int comp(int a, int b) {
	return a > b;
}
void build(int l, int r, int p) {
	if (l == r) return (void) (f[p] = l);
	int mid = l + r >> 1;
	build(l, mid, p2); build(mid + 1, r, p3);
	f[p] = min(f[p2], f[p3]);
}
void down(int p) {
	add[p2] += add[p]; add[p3] += add[p];
	add[p] = 0;
}
void upt(int p) {
	f[p] = min(f[p2] + add[p2], f[p3] + add[p3]);
}
void change(int l, int r, int s, int e, int v, int p) {
	if (l == s && r == e) return (void) (add[p] += v);
	int mid = l + r >> 1;
	down(p);
	if (e <= mid) change(l, mid, s, e, v, p2);
	else if (s >= mid + 1) change(mid + 1, r, s, e, v, p3);
	else change(l, mid, s, mid, v, p2),
		change(mid + 1, r, mid + 1, e, v, p3);
	upt(p);
}
int ask(int l, int r, int k, int p) {
	if (l == r) return f[p] + add[p] >= k ? l : l + 1;
	int mid = l + r >> 1, res;
	down(p);
	if (f[p3] + add[p3] >= k) res = ask(l, mid, k, p2);
	else res = ask(mid + 1, r, k, p3);
	return upt(p), res;
}
int main() {
	int i;
	cin >> n >> k;
	For (i, 1, n) d[i] = read();
	sort(d + 1, d + n + 1, comp);
	For (i, 1, n) fa[i] = floor(1.0 * i / k);
	For (i, 1, n) {
		pre[i] = i;
		if (i > 1 && d[i - 1] == d[i]) pre[i] = pre[i - 1];
	}
	Rof (i, n, 1) {
		nxt[i] = i;
		if (i < n && d[i] == d[i + 1]) nxt[i] = nxt[i + 1];
	}
	For (i, 1, n) sze[i] = 1;
	Rof (i, n, 1) if (fa[i]) sze[fa[i]] += sze[i];
	build(1, n, 1);
	For (i, 1, n) {
		if (fa[i] && fa[i] != fa[i - 1])
			change(1, n, ans[fa[i]], n, sze[fa[i]] - 1, 1);
		int pos = ask(1, n, sze[i], 1), p = pre[pos], q = nxt[p];
		nxt[p]--; ans[i] = q;
		change(1, n, q, n, -sze[i], 1);
	}
	For (i, 1, n) printf("%d ", d[ans[i]]);
	cout << endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/xyz32768/article/details/82804811