striving & singing
ZhengruIOI 提高组十连测 Day2。
http://www.zhengruioi.com/contest/689/problem/1539
每个月可以增加若个次购买某种钻石的机会,那么可以直接贪心,把所有方案按照单价排序,然后每次买最便宜的即可。可以直接用权值线段树维护,复杂度$O(nlogm)$。
#include<cstdio>
#include<algorithm>
const long long INF = 0x7fffffffffffffff;
int n, m, c[200100];
long long sumb[200100], ans;
struct plan {
int a, b;
bool operator < (const plan &b) const { return a < b.a; }
}pls[200100];
inline long long min(long long a, long long b) { return a < b ? a : b; }
struct segtreenode { int asntag; long long sum; bool inf; };
struct segtree {
segtreenode nodes[800100];
inline long long sum(int l, int r, int k) { return (sumb[r] - sumb[l - 1]) * k; }
inline long long rest(int x, int l, int r, int k) { return nodes[x].inf ? INF : sum(l, r, k) - nodes[x].sum; }
inline int lson(int x) { return x << 1; }
inline int rson(int x) { return x << 1 | 1; }
inline void pushup(int x) { nodes[x].sum = nodes[lson(x)].sum + nodes[rson(x)].sum; }
inline void pushdown(int x, int l, int r) {
if (!nodes[x].asntag) return;
int mid = l + (r - l >> 1);
nodes[lson(x)].sum = sum(l, mid, nodes[x].asntag); nodes[rson(x)].sum = sum(mid + 1, r, nodes[x].asntag);
nodes[lson(x)].asntag = nodes[rson(x)].asntag = nodes[x].asntag;
nodes[x].asntag = 0;
}
inline void build(int x, int l, int r) {
if (l == r) {
if (pls[l].b == -1) nodes[x].inf = true;
return;
}
int mid = l + (r - l >> 1);
build(lson(x), l, mid);
build(rson(x), mid + 1, r);
nodes[x].inf = nodes[lson(x)].inf || nodes[rson(x)].inf;
}
void add(int x, int l, int r, const int v, const int k) {
if (v <= 0) return;
if (v >= rest(x, l, r, k)) {
nodes[x].asntag = k;
nodes[x].sum = sum(l, r, k);
return;
}
if (l == r) {
nodes[x].sum += v;
return;
}
pushdown(x, l, r);
int mid = l + (r - l >> 1); long long lv = min(v, rest(lson(x), l, mid, k));
add(lson(x), l, mid, lv, k);
add(rson(x), mid + 1, r, v - lv, k);
pushup(x);
}
long long calc(int x, int l, int r) {
if (l == r) return nodes[x].sum * pls[l].a;
pushdown(x, l, r);
int mid = l + (r - l >> 1);
return calc(lson(x), l, mid) + calc(rson(x), mid + 1, r);
}
}segtree;
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &c[i]);
for (int i = 1; i <= m; i++) scanf("%d%d", &pls[i].a, &pls[i].b);
std::sort(pls + 1, pls + m + 1);
for (int i = 1; i <= m; i++) sumb[i] = sumb[i - 1] + (pls[i].b == -1 ? 0 : pls[i].b);
segtree.build(1, 1, m);
for (int i = 1; i <= n; i++) segtree.add(1, 1, m, c[i], i);
ans = segtree.calc(1, 1, m);
printf("%lld\n", ans);
return 0;
}
http://www.zhengruioi.com/contest/689/problem/1540
显然可以直接排序。
#include<cstdio>
#include<algorithm>
const long long INF = 0x7fffffffffffffff;
int n, m, c[200100];
long long sumb[200100], ans;
struct plan {
int a, b;
bool operator < (const plan &b) const { return a < b.a; }
}pls[200100];
inline long long min(long long a, long long b) { return a < b ? a : b; }
struct segtreenode { int asntag; long long sum; bool inf; };
struct segtree {
segtreenode nodes[800100];
inline long long sum(int l, int r, int k) { return (sumb[r] - sumb[l - 1]) * k; }
inline long long rest(int x, int l, int r, int k) { return nodes[x].inf ? INF : sum(l, r, k) - nodes[x].sum; }
inline int lson(int x) { return x << 1; }
inline int rson(int x) { return x << 1 | 1; }
inline void pushup(int x) { nodes[x].sum = nodes[lson(x)].sum + nodes[rson(x)].sum; }
inline void pushdown(int x, int l, int r) {
if (!nodes[x].asntag) return;
int mid = l + (r - l >> 1);
nodes[lson(x)].sum = sum(l, mid, nodes[x].asntag); nodes[rson(x)].sum = sum(mid + 1, r, nodes[x].asntag);
nodes[lson(x)].asntag = nodes[rson(x)].asntag = nodes[x].asntag;
nodes[x].asntag = 0;
}
inline void build(int x, int l, int r) {
if (l == r) {
if (pls[l].b == -1) nodes[x].inf = true;
return;
}
int mid = l + (r - l >> 1);
build(lson(x), l, mid);
build(rson(x), mid + 1, r);
nodes[x].inf = nodes[lson(x)].inf || nodes[rson(x)].inf;
}
void add(int x, int l, int r, const int v, const int k) {
if (v <= 0) return;
if (v >= rest(x, l, r, k)) {
nodes[x].asntag = k;
nodes[x].sum = sum(l, r, k);
return;
}
if (l == r) {
nodes[x].sum += v;
return;
}
pushdown(x, l, r);
int mid = l + (r - l >> 1); long long lv = min(v, rest(lson(x), l, mid, k));
add(lson(x), l, mid, lv, k);
add(rson(x), mid + 1, r, v - lv, k);
pushup(x);
}
long long calc(int x, int l, int r) {
if (l == r) return nodes[x].sum * pls[l].a;
pushdown(x, l, r);
int mid = l + (r - l >> 1);
return calc(lson(x), l, mid) + calc(rson(x), mid + 1, r);
}
}segtree;
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &c[i]);
for (int i = 1; i <= m; i++) scanf("%d%d", &pls[i].a, &pls[i].b);
std::sort(pls + 1, pls + m + 1);
for (int i = 1; i <= m; i++) sumb[i] = sumb[i - 1] + (pls[i].b == -1 ? 0 : pls[i].b);
segtree.build(1, 1, m);
for (int i = 1; i <= n; i++) segtree.add(1, 1, m, c[i], i);
ans = segtree.calc(1, 1, m);
printf("%lld\n", ans);
return 0;
}
http://www.zhengruioi.com/contest/689/problem/1541
到现在还没读懂题,以后再补。
ZhengruIOI 普转提七连测 Day1。
http://www.zhengruioi.com/contest/690/problem/1535
可以直接$O(n)$模拟这个命令序列,得到这个序列对应的位移向量,然后再做$t$次向量加法即可,复杂度$O(n+t)$。
上面是我的思路,题解的思路是因为$4a\bmod 4=0,a\in N$所以每执行$4$次命令序列后会回到原来的方向,然后模拟$4$次命令序列,再对$t\bmod 4$的零散部分模拟即可。
下面是我的思路的代码。
#include<cstdio>
const int vct[4][2] = { {0, 1}, {1, 0}, {0, -1}, {-1, 0} };
const int vct2[4][2] = { {1, 1}, {1, -1}, {-1, -1}, {-1, 1} };
int n, t, dir;
long long x, y;
inline long long abs(long long x) { return x > 0 ? x : -x; }
int main() {
scanf("%d%d", &n, &t);
for (int i = 1; i <= n; i++) {
int a; scanf("%d", &a);
x += a * vct[dir][0], y += a * vct[dir][1];
dir = (dir + a) % 4;
}
long long x1 = x, y1 = y; int dir1 = dir;
x = 0, y = 0, dir = 0;
for (int i = 1; i <= t; i++) x += x1 * vct2[dir][0], y += y1 * vct2[dir][1], dir = (dir + dir1) % 4;
printf("%lld\n", abs(x) + abs(y));
return 0;
}
http://www.zhengruioi.com/contest/690/problem/1536
可以枚举最后走到了哪个餐馆,设最后走到了第$i$个餐馆,那么现在要找到一个元素个数最大的集合${k\in N|k\in [1,i]}$使得元素的$t_k$之和小于等于$m-x_i$。显然直接贪心找$t_k$最小的那些位置是最优的,可以用一个大根堆维护,每枚举一个位置$i$就把这个位置$i$加入到堆中,然后一直 pop 直到堆中位置的$t_k$之和小于等于$m-x_i$。
#include<cstdio>
#include<queue>
#include<algorithm>
int n, ans, cnt;
long long m, sum;
struct res {
long long x; int t;
bool operator < (const res &b) const { return x < b.x; }
}rs[100100];
std::priority_queue<int> q;
inline int max(int a, int b) { return a > b ? a : b; }
int main() {
scanf("%d%lld", &n, &m);
for (int i = 1; i <= n; i++) scanf("%lld%d", &rs[i].x, &rs[i].t);
std::sort(rs + 1, rs + n + 1);
for (int i = 1; i <= n; i++) {
if (rs[i].x > m) break;
long long x = rs[i].x;
q.push(rs[i].t); sum += rs[i].t; cnt++;
while (sum > m - x) {
sum -= q.top(); cnt--;
q.pop();
}
ans = max(ans, cnt);
}
printf("%d\n", ans);
return 0;
}
http://www.zhengruioi.com/contest/690/problem/1537
因为有第一种操作所以可以把问题看作环上的问题,然后就不用管第一种操作了。接下来有个结论,一个符卡集合能够全部使用的充要条件是这个集合的$l_i$之和小于等于$n$。必要性在于如果$\sum l_i> n$那么显然没法全部使用。充分性的证明可以用归纳法,设一个符卡到下一个符卡的距离为$d_i$,那么因为$\sum d_i=n,\sum l_i\le n$所以由抽屉原理总存在一个$i$使得$l_i\le s_i$,那么这张符卡就可以使用。
然后问题就转化为了 01 背包问题,背包大小为$n$。
#include<cstdio>
int n, l[1010], d[1010], dp[1010], ans;
inline int max(int a, int b) { return a > b ? a : b; }
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &l[i]);
for (int i = 1; i <= n; i++) scanf("%d", &d[i]);
for (int i = 1; i <= n; i++)
for (int j = n; j >= 0; j--)
if (j >= l[i]) dp[j] = max(dp[j], dp[j - l[i]] + d[i]);
for (int i = 1; i <= n; i++) ans = max(ans, dp[i]);
printf("%d\n", ans);
return 0;
}
http://www.zhengruioi.com/contest/690/problem/1538
可以维护所有不同种类的集合,以及一个集合$S$蕴含所有不同种类的集合中前$t$大的数,然后每次输出$S$中前$n$大的数即可。可以用动态开点权值线段树维护集合。
#include<cstdio>
#include<algorithm>
int n, m, pv[300100], cnt;
struct operation {
char opt; int t, p;
}opts[300100];
inline int min(int a, int b) { return a < b ? a : b; }
inline int max(int a, int b) { return a > b ? a : b; }
struct segtreenode { int lson, rson, cnt; long long sum; }nodes[6000100];
struct segtree {
int root;
inline void pushup(int x) {
nodes[x].cnt = nodes[nodes[x].lson].cnt + nodes[nodes[x].rson].cnt;
nodes[x].sum = nodes[nodes[x].lson].sum + nodes[nodes[x].rson].sum;
}
int insert(int x, int l, int r, int v) {
if (v == -1) return x;
if (!x) x = ++cnt;
if (l == r) {
nodes[x].cnt++; nodes[x].sum += pv[v];
return x;
}
int mid = l + (r - l >> 1);
if (v <= mid) nodes[x].lson = insert(nodes[x].lson, l, mid, v);
else if (mid + 1 <= v) nodes[x].rson = insert(nodes[x].rson, mid + 1, r, v);
pushup(x);
return x;
}
void remove(int x, int l, int r, int v) {
if (!x || v == -1) return;
if (l == r) {
nodes[x].cnt--; nodes[x].sum -= pv[v];
return;
}
int mid = l + (r - l >> 1);
if (v <= mid) remove(nodes[x].lson, l, mid, v);
else if (mid + 1 <= v) remove(nodes[x].rson, mid + 1, r, v);
pushup(x);
}
int rnk(int x, int l, int r, int v) {
if (l == r) return 1;
int mid = l + (r - l >> 1);
if (v <= mid) return rnk(nodes[x].lson, l, mid, v);
else if (mid + 1 <= v) return nodes[nodes[x].lson].cnt + rnk(nodes[x].rson, mid + 1, r, v);
}
int kth(int x, int l, int r, int k) {
if (l == r) return l;
int mid = l + (r - l >> 1);
if (k <= nodes[nodes[x].lson].cnt) return kth(nodes[x].lson, l, mid, k);
else if (k <= nodes[x].cnt) return kth(nodes[x].rson, mid + 1, r, k - nodes[nodes[x].lson].cnt);
else return -1;
}
long long ksum(int x, int l, int r, int k) {
k = min(k, nodes[x].cnt);
if (l == r) return pv[l] * k;
int mid = l + (r - l >> 1);
if (k <= nodes[nodes[x].lson].cnt) return ksum(nodes[x].lson, l, mid, k);
else return nodes[nodes[x].lson].sum + ksum(nodes[x].rson, mid + 1, r, k - nodes[nodes[x].lson].cnt);
}
}tr[300100], s;
inline bool cmp(int a, int b) { return a > b; }
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) {
char opt = getchar(), c;
while (!('A' <= opt && opt <= 'Z')) opt = getchar();
c = getchar();
while ('A' <= c && c <= 'Z') c = getchar();
int t, p; scanf("%d%d", &t, &p);
opts[i].opt = opt; opts[i].t = t; opts[i].p = p;
pv[i] = p;
}
std::sort(pv + 1, pv + m + 1, cmp);
int pn = std::unique(pv + 1, pv + m + 1) - 1 - pv;
for (int i = 1; i <= m; i++) opts[i].p = std::lower_bound(pv + 1, pv + pn + 1, opts[i].p, cmp) - pv;
for (int i = 1; i <= m; i++) {
char &opt = opts[i].opt; int &t = opts[i].t, &p = opts[i].p;
if (opt == 'B') {
tr[t].root = tr[t].insert(tr[t].root, 1, pn, p);
int k = tr[t].rnk(tr[t].root, 1, pn, p);
if (k <= t) {
s.root = s.insert(s.root, 1, pn, p);
if (k == t) s.remove(s.root, 1, pn, tr[t].kth(tr[t].root, 1, pn, t + 1));
}
}
else if (opt == 'R') {
int k = tr[t].rnk(tr[t].root, 1, pn, p);
if (k <= t) {
s.remove(s.root, 1, pn, p);
if (k == t) s.root = s.insert(s.root, 1, pn, tr[t].kth(tr[t].root, 1, pn, t + 1));
}
tr[t].remove(tr[t].root, 1, pn, p);
}
printf("%lld\n", s.ksum(s.root, 1, pn, n));
}
return 0;
}
return