闲话 22.10.23
闲话
打”细节”,然后输入了“xj”,第一候选字是“星界”
aaaa我想听情绪的歌了aaaa
奇怪的戒断反应增加了
smtwy:我手机里有一万多张二次元图
我:您是在什么精神状态下做出这种事的
打了cf
Runtime error on pretest 2
哦我vector多弹了个元素
改掉应该就没事了
Wrong answer on pretest 2
Wrong answer on pretest 2
Wrong answer on pretest 2
azazazazaz
于是就这么在C2吃了四发罚时
不要死磕已经知道fake的思路(
[最近脑子里一直循环Aster的pv,所以今天推的就是Aster了!]
随机刷题
有 \(x+y+z\) 个人,第 \(i\) 个人有 \(A_i\) 个金币,$ B_i$ 个银币,\(C_i\) 个铜币。
选出 \(x\) 个人获得其金币,选出 \(y\) 个人获得其银币,选出 \(z\) 个人获得其铜币,在不重复选某个人的情况下,最大化获得的币的总数。
\(x+y+z\le 1e5\)。
可反悔贪心。
首先我们先假设从所有人那里都拿了金币。随后将一个人拿银币的收益转换成 \(B_i - A_i\),拿铜币的收益转换成 \(C_i - A_i\)。
然后我们就可以转化成 \(n\) 个二元组 \((a_i, b_i)\),从中里选择 \(y\) 个加入 \(a_i\),选出 \(z\) 个加入 \(b_i\),最后的总价值最大是多少。
考虑通过一些方式产生一个断点,使得在这前面的点都选 \(a_i\),在这后面的点都选 \(b_i\)。
假设 \((a_i, b_i)\) 选 \(a_i\),\((a_j,b_j)\) 选 \(b_j\),并且交换后答案不会更优,则有
移项后得到
通过此关键字排序产生断点即可。
然后处理每个点前缀中选 \(y\) 个,后缀中选 \(z\) 个的最大花费,最后 \(O(n)\) 枚举即可。
处理前后缀时使用堆维护当前所选元素中的最小值。总时间复杂度 \(O(n\log n)\)。
细节:不要从 \(1\) 开始枚举要不然会吃很多次罚时
code
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
using namespace std;
char buf[1<<21], *p1 = buf, *p2 = buf; inline char getc() { return (p1 == p2 and (p2 = (p1 = buf) + fread(buf, 1, 1<<21, stdin), p1 == p2) ? EOF : *p1++); }
#define getchar getc
template <typename T> inline void get(T & x){
x = 0; char ch = getchar(); bool f = 0; while (ch < '0' or ch > '9') f = f or ch == '-', ch = getchar();
while (ch >= '0' and ch <= '9') x = (x<<1) + (x<<3) + (ch^48), ch = getchar(); f and (x = -x);
} template <typename T, typename ... Args> inline void get(T & x, Args & ... _Args) { get(x); get(_Args...); }
#define rep(i,a,b) for (register int i = (a), i##_ = (b) + 1; i < i##_; ++i)
#define pre(i,a,b) for (register int i = (a), i##_ = (b) - 1; i > i##_; --i)
#define int long long
const int N = 1e5 + 10;
int n, x, y, z, sum1[N], sum2[N], ans;
struct nin {
int a, b, c;
int b_a, c_a;
bool operator < (const nin & s) const {
return b_a - c_a > s.b_a - s.c_a;
}
} a[N];
priority_queue <int, vector<int>, greater<> > que;
signed main() {
get(x, y, z); n = x + y + z;
rep(i,1,n) get(a[i].a, a[i].b, a[i].c), a[i].b_a = a[i].b - a[i].a, a[i].c_a = a[i].c - a[i].a, ans += a[i].a;
sort(a+1, a+1+n);
int now = 0;
rep(i,1,n) {
if (que.size() < y) now += a[i].b_a, que.push(a[i].b_a);
else if (que.top() < a[i].b_a) {
now += a[i].b_a - que.top();
que.pop(); que.push(a[i].b_a);
} sum1[i] = now;
}
now = 0; while (que.size()) que.pop();
pre(i,n,1) {
if (que.size() < z) now += a[i].c_a, que.push(a[i].c_a);
else if (que.top() < a[i].c_a) {
now += a[i].c_a - que.top();
que.pop(); que.push(a[i].c_a);
} sum2[i] = now;
}
now = - (1e9 + 19);
rep(i,y,n-z) now = max(now, ans + sum1[i] + sum2[i+1]);
cout << now << endl;
}
有 \(n\) 个学生,每人有两种技能,分别是 \(a,b\) 表示编程能力和运动能力。你需要将他们分为两个团队分别参加编程比赛和体育比赛,编程团队有 \(p\) 人,体育团队有 \(s\) 人,一个学生只能参加其中一项。每个团队的力量是其成员在对应领域能力总和,请问如何分配能使得两个团队的能力和最大?输出最大能力和与任意一种方案。
原译者没分段不是我的问题
首先贪心地让 \(a\) 最大的 \(p\) 个人进入编程团队,然后进行 \(s\) 次抉择,在保证编程团队人数不减的情况下让一个人加入体育团队。
两种情况:
- 将一个不在团队里的人放入体育团队,增加 \(b_i\)
- 将一个原来在编程团队里的人放入体育团队并将一个不在团队里的人放入编程团队,增加 \(b_i - a_i + a_j\)
维护 \(a_i,b_i,b_i-a_i\) 的最大值,每次取最优决策执行即可。
使用三个堆,总时间复杂度 \(O(n \log n)\)。
code
#include <bits/stdc++.h>
using namespace std;
char buf[1<<21], *p1 = buf, *p2 = buf; inline char getc() { return (p1 == p2 and (p2 = (p1 = buf) + fread(buf, 1, 1<<21, stdin), p1 == p2) ? EOF : *p1++); }
#define getchar getc
template <typename T> inline void get(T & x){
x = 0; char ch = getchar(); bool f = 0; while (ch < '0' or ch > '9') f = f or ch == '-', ch = getchar();
while (ch >= '0' and ch <= '9') x = (x<<1) + (x<<3) + (ch^48), ch = getchar(); f and (x = -x);
} template <typename T, typename ... Args> inline void get(T & x, Args & ... _Args) { get(x); get(_Args...); }
#define rep(i,a,b) for (register int i = (a), i##_ = (b) + 1; i < i##_; ++i)
#define pre(i,a,b) for (register int i = (a), i##_ = (b) - 1; i > i##_; --i)
const int N = 3e5 + 10;
int n, x, y, ans;
int a[N], b[N];
pair<int,int> w[N];
int bel[N];
priority_queue <pair<int,int> > q1, q2, q3;
#define insert(u) \
{\
if (bel[u] == 0) q1.emplace(b[u], u), q2.emplace(a[u], u);\
else if (bel[u] == 1) q3.emplace(b[u] - a[u], u);\
}
signed main() {
get(n, x, y);
rep(i,1,n) get(a[i]), w[i] = {a[i], i};
rep(i,1,n) get(b[i]);
sort(w+1, w+1+n, greater<>());
rep(i,1,x) ans += w[i].first, bel[w[i].second] = 1;
rep(i,1,n) insert(i);
rep(i,1,y) {
int mx = -1e9, typ = 0;
while (!q1.empty() and bel[q1.top().second] != 0) q1.pop();
while (!q2.empty() and bel[q2.top().second] != 0) q2.pop();
while (!q3.empty() and bel[q3.top().second] != 1) q3.pop();
if (q1.size() and mx < q1.top().first) mx = q1.top().first, typ = 1;
if (q2.size() and q3.size() and mx < q2.top().first + q3.top().first) mx = q2.top().first + q3.top().first, typ = 2;
assert(typ > 0);
ans += mx;
if (typ == 1) { bel[q1.top().second] = 2; insert(q1.top().second); }
else { bel[q2.top().second] = 1, bel[q3.top().second] = 2; insert(q2.top().second); insert(q3.top().second); }
}
cout << ans << endl;
rep(i,1,n) if (bel[i] == 1) cout << i << ' '; cout << endl;
rep(i,1,n) if (bel[i] == 2) cout << i << ' '; cout << endl;
}
\(n\) 道题, 第 \(i\) 天可以花费 \(a_i\) 准备一道题, 花费 \(b_i\) 打印一道题, 每天最多准备一道, 最多打印一道, 准备的题可以留到以后打印, 求最少花费使得准备并打印 \(k\) 道题。
\(n \le 5\times 10^5\)。
wqs二分。
考虑我们令准备一道题的花费减少 \(\text{mid}\)。只有在准备并打印一道题的花费小于 \(0\) 时我们才计入答案。然后可以计算出对于 \(\text{mid}\) 共能够完成多少道题。
当完成题数刚好等于 \(m\) 时得到答案。
code
#include <bits/stdc++.h>
using namespace std;
#define int long long
#ifdef ONLINE_JUDGE
char buf[1<<21], *p1 = buf, *p2 = buf; inline char getc() { return (p1 == p2 and (p2 = (p1 = buf) + fread(buf, 1, 1<<21, stdin), p1 == p2) ? EOF : *p1++); }
#define getchar getc
#endif
template <typename T> inline void get(T & x){
x = 0; char ch = getchar(); bool f = 0; while (ch < '0' or ch > '9') f = f or ch == '-', ch = getchar();
while (ch >= '0' and ch <= '9') x = (x<<1) + (x<<3) + (ch^48), ch = getchar(); f and (x = -x);
} template <typename T, typename ... Args> inline void get(T & x, Args & ... _Args) { get(x); get(_Args...); }
#define rep(i,a,b) for (register int i = (a), i##_ = (b) + 1; i < i##_; ++i)
#define pre(i,a,b) for (register int i = (a), i##_ = (b) - 1; i > i##_; --i)
const int N = 5e5 + 10; using ll = long long;
int n, k, ret, cnt, a[N], b[N], cb[N];
void check(int val){
priority_queue <pair<ll, int>, vector<pair<ll, int> >, greater<> > que;
ret = cnt = 0; int dlt;
rep(i,1,n) {
que.emplace(a[i] - val, 1);
dlt = b[i] + que.top().first;
if (dlt < 0) {
ret += dlt; cnt += que.top().second;
que.pop();
que.emplace(-b[i], 0);
}
}
}
signed main() {
get(n, k); rep(i,1,n) get(a[i]); rep(i,1,n) get(b[i]);
int l = 0, r = 2e9, mid;
while (l < r) {
mid = l + r >> 1;
check(mid);
if (cnt == k) { cout << ret + k * mid; return 0; }
else if (cnt > k) r = mid - 1;
else l = mid + 1;
}
cout << (ret / cnt + mid) * k;
}
给定一个长度为 \(n\) 的序列 \(a_i\)。
有 \(q\) 次询问 \((l,r,k)\):输出 \(a_l\) 到 \(a_r\) 中大于 \(k\) 的数字的个数。
区间允许离线的信息,考虑莫队维护。
左端点分块,块长设 \(\frac {n}{\sqrt q}\) 即可。
以此块长分块可以得到端点移动次数 \(O(q\sqrt n + q\sqrt q)\)。分析可得此为最优选择。
右端点奇偶化排序降低常数。
我们发现维护的信息只需要相对大小关系不变即可,因此可以离散化使得值域落在 \(O(n)\) 范围内。
考虑莫队不平衡的信息维护情况。
在整个程序中,我们一共需要移动端点 \(O(q \sqrt q)\) 次,但只需要查询 \(O(q)\) 次。这启发我们采用根号平衡思想,设计一种 \(O(1)\) 修改 \(O(\sqrt n)\) 查询的数据结构。
考虑值域分块。对值域分块,长度为 \(O(\sqrt n)\),维护单点数量和块内加和。在加入时修改所在点的值与所在块的加和,查询时扫描其所在块内其后元素,并扫描所在块后的块即可。
总时间复杂度 \(O(q \sqrt q)\)。
实现时可以只离散化 \(a_i\),\(k\) 值在需要时查询 \(\text{upper_bound}\) 的前驱即可。
code
#include <bits/stdc++.h>
using namespace std;
#ifdef ONLINE_JUDGE
char buf[1<<21], *p1 = buf, *p2 = buf; inline char getc() { return (p1 == p2 and (p2 = (p1 = buf) + fread(buf, 1, 1<<21, stdin), p1 == p2) ? EOF : *p1++); }
#define getchar getc
#endif
template <typename T> inline void get(T & x){
x = 0; char ch = getchar(); bool f = 0; while (ch < '0' or ch > '9') f = f or ch == '-', ch = getchar();
while (ch >= '0' and ch <= '9') x = (x<<1) + (x<<3) + (ch^48), ch = getchar(); f and (x = -x);
} template <typename T, typename ... Args> inline void get(T & x, Args & ... _Args) { get(x); get(_Args...); }
#define rep(i,a,b) for (register int i = (a), i##_ = (b) + 1; i < i##_; ++i)
#define pre(i,a,b) for (register int i = (a), i##_ = (b) - 1; i > i##_; --i)
const int N = 3e5 + 10;
int n, q, siz, bel[N], a[N], lsh[N], hrt, ans[N];
struct query {
int l, r, k, id;
bool operator < (const query & b) const {
if (bel[l] != bel[b.l]) return l < b.l;
if (bel[l] & 1) return r > b.r;
return r < b.r;
}
} qy[N];
struct sqrt_tech {
int L, siz, bel[N], lp[N], rp[N], cntpts[N], cntblk[N];
void init(int _l) {
L = _l;
siz = sqrt(L);
rep(i,1,L) {
bel[i] = (i - 1) / siz + 1;
if (bel[i] != bel[i-1]) lp[bel[i]] = i, rp[bel[i-1]] = i-1;
} rp[bel[L]] = L;
}
void add(int val, int k) {
cntpts[val] += k ; cntblk[bel[val]] += k;
}
int qry(int k) {
int ret = 0;
rep(i,k+1,rp[bel[k]]) ret += cntpts[i];
rep(i,bel[k]+1,bel[L]) ret += cntblk[i];
return ret;
}
} st;
signed main() {
get(n); rep(i,1,n) get(a[i]), lsh[++hrt] = a[i];
get(q); rep(i,1,q) get(qy[i].l, qy[i].r, qy[i].k), qy[i].id = i;
siz = n / sqrt(q);
rep(i,1,n) bel[i] = (i - 1) / siz + 1;
sort(qy + 1, qy + 1 + q);
sort(lsh+1, lsh+1+hrt); hrt = unique(lsh+1, lsh+1+hrt) - lsh - 1;
rep(i,1,n) a[i] = lower_bound(lsh+1, lsh+1+hrt, a[i]) - lsh;
st.init(hrt);
for (int i(1), l(1), r(0); i <= q; ++ i) {
while (l > qy[i].l) st.add(a[-- l], 1);
while (r < qy[i].r) st.add(a[++ r], 1);
while (l < qy[i].l) st.add(a[l ++], -1);
while (r > qy[i].r) st.add(a[r --], -1);
int tmp = upper_bound(lsh+1, lsh+1+hrt, qy[i].k) - lsh - 1;
ans[qy[i].id] = st.qry(tmp);
}
rep(i,1,q) cout << ans[i] << '\n';
}
双倍经验。昨天数据备份那题。
以下是博客签名,与正文无关。
请按如下方式引用此页:
本文作者 joke3579,原文链接:https://www.cnblogs.com/joke3579/p/chitchat221023.html。
遵循 CC BY-NC-SA 4.0 协议。
请读者尽量不要在评论区发布与博客内文完全无关的评论,视情况可能删除。