題意
Sol
不難發現題目給出的是一個樹,其中(frac{i}{K})是(i)的父親節點
首先,當(d_i)互不相同時,一個顯然的貪心策略就是優先給編號小的分配較大的權值。可以排序後dfs完成。
但是,當(d_i)相同時,可能存在這樣一種情況:把編號小的子樹內權值較大的節點,和某個編號較大的根交換後,仍然滿足要求
比如(N = 4, K = 2, a = {1, 1, 1, 2})
此時直接貪心的話會輸出(1, 1, 1, 2),實際上最優解為(1, 1, 2, 1)
這時候怎麼辦呢?
考慮一個節點(p)可以選擇權值為(x)的條件是什麼,因為該節點子樹內的權值一定都比(x)大
因此對於每個權值小於(x)的數,權值比它大且可以選擇的數至少為(siz[p])個
同時根據貪心的策略,先遍歷到的節點應該選大的權值。
這樣就不難得到一個演算法:
首先按權值從大到小排序,同時用線段樹維護出每個位置權值比它大且能選擇的位置個數
對於每個點(p),線上段樹上二分出最大的滿足條件的位置(x)。同時,當權值相同時,該位置應該更靠右。
然後再在區間([x, n])中的所有點減去(siz[x])
注意一個細節,當遍歷到某個節點時,應該消去父親對他的影響
寫完程式碼 -> 過樣例 -> 1A hhhhhh(雖然是抄的)
60分
#include<bits/stdc++.h>
#define sz(x) (int)x.size()
using namespace std;
const int MAXN = 5e5 + 10;
inline int read() {
char c = getchar(); int x = 0, f = 1;
while(c < `0` || c > `9`) {if(c == `-`) f = -1; c = getchar();}
while(c >= `0` && c <= `9`) x = x * 10 + c - `0`, c = getchar();
return x * f;
}
int N; double K;
int a[MAXN], ans[MAXN], res;
vector<int> v[MAXN];
int AddEdge(int x, int y) {
v[x].push_back(y);
}
void dfs(int x) {
for(int i = 0; i < sz(v[x]); i++) dfs(v[x][i]);
if(x) ans[x] = a[res--];
}
int main() {
scanf("%d%lf", &N, &K); res = N;
//printf("%lf
", K);
for(int i = 1; i <= N; i++) {
int pre = (int) floor(i / K);
a[i] = read(); AddEdge(pre, i);
}
sort(a + 1, a + N + 1);
dfs(0);
for(int i = 1; i <= N; i++) printf("%d ", ans[i]);
return 0;
}
AC程式碼
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 5e5 + 10;
inline int read() {
char c = getchar(); int x = 0, f = 1;
while(c < `0` || c > `9`) {if(c == `-`) f = -1; c = getchar();}
while(c >= `0` && c <= `9`) x = x * 10 + c - `0`, c = getchar();
return x * f;
}
int N; double K;
int cnt[MAXN], fa[MAXN], siz[MAXN], ans[MAXN], a[MAXN];
#define ls k << 1
#define rs k << 1 | 1
struct Node {
int l, r, f, mn;
}T[MAXN << 2];
void update(int k) {
T[k].mn = min(T[ls].mn, T[rs].mn);
}
void add(int k, int val) {
T[k].f += val; T[k].mn += val;
}
void pushdown(int k) {
if(!T[k].f) return ;
add(ls, T[k].f); add(rs, T[k].f);
T[k].f = 0;
}
void Build(int k, int ll, int rr) {
T[k] = (Node) {ll, rr, 0, 0};
if(ll == rr) {T[k].mn = ll; return ;}
int mid = ll + rr >> 1;
Build(ls, ll, mid); Build(rs, mid + 1, rr);
update(k);
}
void IntervalAdd(int k, int ll, int rr, int val) {
if(ll <= T[k].l && T[k].r <= rr) {add(k, val); return ;}
pushdown(k);
int mid = T[k].l + T[k].r >> 1;
if(ll <= mid) IntervalAdd(ls, ll, rr, val);
if(rr > mid) IntervalAdd(rs, ll, rr, val);
update(k);
}
int Query(int k, int sz) {
if(T[k].l == T[k].r) return T[k].mn >= sz ? T[k].l : T[k].l + 1;
pushdown(k);
if(T[rs].mn >= sz) return Query(ls, sz);
else return Query(rs, sz);
}
int main() {
N = read(); cin >> K;
for(int i = 1; i <= N; i++) {
a[i] = read();
int t = (int) floor(i / K); fa[i] = t; siz[i] = 1;
}
for(int i = N; i >= 0; i--) siz[fa[i]] += siz[i];
Build(1, 1, N);
sort(a + 1, a + N + 1, greater<int>());
for(int i = N - 1; i >= 1; i--) cnt[i] = (a[i] == a[i + 1] ? cnt[i + 1] + 1 : 0);
for(int i = 1; i <= N; i++) {
if(fa[i] && fa[i] != fa[i - 1]) IntervalAdd(1, ans[fa[i]], N, siz[fa[i]] - 1);
int t = Query(1, siz[i]); t += cnt[t]; cnt[t]++; t -= (cnt[t] - 1);
ans[i] = t;
IntervalAdd(1, t, N, -siz[i]);
}
for(int i = 1; i <= N; i++) printf("%d ", a[ans[i]]);
return 0;
}