BZOJ5249: [2018多省省队联测]IIIDX(线段树 贪心)
程序员文章站
2022-03-10 15:13:13
题意 "题目链接" Sol 不难发现题目给出的是一个树,其中$\frac{i}{K}$是$i$的父亲节点 首先,当$d_i$互不相同时,一个显然的贪心策略就是优先给编号小的分配较大的权值。可以排序后dfs完成。 但是,当$d_i$相同时,可能存在这样一种情况:把编号小的子树内权值较大的节点,和某个编 ......
题意
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\n", 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; }
上一篇: 详解C++ 的STL迭代器原理和实现
下一篇: C++浮点数在内存中的存储详解