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