CLYZ-NOIP十连测 Day2
CLYZ-NOIP2021 国庆集训 B 组 Day2
题面:https://files.cnblogs.com/files/blogs/575626/day2B.zip
子段的和
这种问题一般就是每次选出个最大的来,然后剩余部分扩展出新的最优解即可。
对于这道题,我们用一个堆,首先存下来 \([1,i]\) 这些段的和,然后每次取出来一个 \([l,r]\) 的时候,把 \([l+1,r]\) 放进去即可。
#include <bits/stdc++.h>
using std::cin;
using std::cout;
using std::priority_queue;
using std::vector;
const int N = 1e5 + 10;
struct Node {
int l, r;
long long sum;
bool operator < (const Node &a) const {
return this->sum < a.sum;
}
Node (int L = 0, int R = 0, long long S = 0) : l(L), r(R), sum(S) {}
};
priority_queue<Node> Q;
int n, w;
long long a[N], S;
int main() {
freopen("wsum.in", "r", stdin);
freopen("wsum.out", "w", stdout);
std::ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> w;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
S += a[i];
Q.push(Node(1, i, S));
}
for (int i = 1; i <= w; ++i) {
auto tp = Q.top();
Q.pop();
cout << tp.sum << ' ';
if (tp.l < tp.r)
Q.push(Node(tp.l + 1, tp.r, tp.sum - a[tp.l]));
}
cout << '\n';
return 0;
}
序列修改
我们考虑啊,分析一下复杂度啊。
如果直接用链表维护的话,前五个操作都是 \(O(1)\) 的,最后一个操作只要改成启发式的就好了,时间复杂度为 \(O(m\log m)\)
当然我直接采用了这个Treap,然后就是个维护数列的板子。
#pragma GCC optimize(2)
#include <bits/stdc++.h>
using std::cin;
using std::cout;
using std::deque;
using std::pair;
using std::make_pair;
const int N = 1e6 + 10, mod = 1e9 + 7;
int n, m, tot, rt[N];
struct Node {
int son[2], tag, val, sz;
} nd[N];
#define ls(k) nd[k].son[0]
#define rs(k) nd[k].son[1]
#define tag(k) nd[k].tag
#define val(k) nd[k].val
#define sz(k) nd[k].sz
inline int Mod(int x) {
if (x >= mod) {
return x - mod;
}
else if (x < 0) {
return x + mod;
}
else {
return x;
}
}
inline int nN(int V) {
int id = ++tot;
val(id) = V;
sz(id) = 1;
return id;
}
inline void maintain(int k) {
sz(k) = sz(ls(k)) + sz(rs(k)) + 1;
}
inline long long Rand() {
return(rand() << 15) | rand();
}
inline void puttag(int k, int x) {
if (k) {
val(k) = Mod(val(k) + x);
tag(k) = Mod(tag(k) + x);
}
}
inline void down(int k) {
if (tag(k)) {
puttag(ls(k), tag(k));
puttag(rs(k), tag(k));
tag(k) = 0;
}
}
int merge(int x, int y) {
if (!x || !y) {
return x + y;
}
if (Rand() % (sz(x) + sz(y)) < sz(x)) {
down(x);
rs(x) = merge(rs(x), y);
maintain(x);
return x;
}
else {
down(y);
ls(y) = merge(x, ls(y));
maintain(y);
return y;
}
}
pair<int, int> split(int u, int k) {
if (!u) {
return make_pair(0, 0);
}
pair<int, int> t;
down(u);
if (k <= sz(ls(u))) {
t = split(ls(u), k);
ls(u) = t.second;
maintain(t.second = u);
}
else {
t = split(rs(u), k - sz(ls(u)) - 1);
rs(u) = t.first;
maintain(t.first = u);
}
return t;
}
int main() {
freopen("sequence.in", "r", stdin);
freopen("sequence.out", "w", stdout);
std::ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
srand(time(0));
for (int i = 1, op, x, y; i <= m; ++i) {
cin >> op;
if (op == 1) {
cin >> x >> y;
rt[x] = merge(nN(y), rt[x]);
}
else if (op == 2) {
cin >> x;
pair<int, int> t = split(rt[x], 1);
rt[x] = t.second;
cout << val(t.first) << '\n';
}
else if (op == 3) {
cin >> x >> y;
rt[x] = merge(rt[x], nN(y));
}
else if (op == 4) {
cin >> x;
pair<int, int> t = split(rt[x], sz(rt[x]) - 1);
rt[x] = t.first;
cout << val(t.second) << '\n';
}
else if (op == 5) {
cin >> x >> y;
puttag(rt[x], y);
}
else {
cin >> x >> y;
rt[x] = merge(rt[x], rt[y]);
rt[y] = 0;
}
}
return 0;
}
最小路费
这个东西就是很明显的类似于换根dp的类型,我们一开始通过一次 \(dfs\) 首先算出来这个 \(1\) 节点选择的答案,并对于每个城市处理三个值。
- \(ret[x]\) \(x\) 子树里的点的运动员全跑到 \(x\) 的费用之和
- \(len[x]\) \(x\) 子树里的点的运动员全跑到 \(x\) 的路程之和
- \(sz[x]\) \(x\) 子树里的运动员的数量之和
最后再做一次 \(dfs\) ,并在 \(dfs\) 的过程中维护他子树以外的点对他的以上三个量,统计答案即可。 \((x+l)^2=x^2+l^2+2xl\)
#include <bits/stdc++.h>
using std::cin;
using std::cout;
using std::make_pair;
using std::pair;
using std::vector;
const int N = 2e5 + 10;
int n;
long long ans = 0x3f3f3f3f3f3f3f3f, c[N], sz[N], ret[N], len[N];
vector<pair<int, long long>> v[N];
long long sq(long long x) {
return x * x;
}
void dfs1(int u, int fa) {
sz[u] = c[u];
for (auto &j: v[u]) {
if (fa != j.first) {
dfs1(j.first, u);
sz[u] += sz[j.first];
ret[u] += ret[j.first] + sz[j.first] * sq(j.second) + 2 * j.second * len[j.first];
len[u] += len[j.first] + j.second * sz[j.first];
}
}
}
void dfs2(int u, long long RET, long long LEN, long long SZ, int fa) {
ans = std::min(ans, ret[u] + RET);
for (auto &j: v[u]) {
if (j.first != fa) {
long long nSZ = SZ + sz[u] - sz[j.first], nLEN = LEN + nSZ * j.second + len[u] - len[j.first] - j.second * sz[j.first], nRET = RET + (ret[u] - ret[j.first] - sz[j.first] * sq(j.second) - 2 * j.second * len[j.first]) + nSZ * sq(j.second) + 2 * (nLEN - nSZ * j.second) * j.second;
dfs2(j.first, nRET, nLEN, nSZ, u);
}
}
return;
}
int main() {
freopen("fare.in", "r", stdin);
freopen("fare.out", "w", stdout);
std::ios::sync_with_stdio(0);
cin.tie(0);
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> c[i];
}
long long z;
for (int i = 1, x, y; i < n; ++i) {
cin >> x >> y >> z;
v[x].emplace_back(y, z);
v[y].emplace_back(x, z);
}
dfs1(1, 0);
dfs2(1, 0, 0, 0, 0);
cout << ans << '\n';
return 0;
}
框选问题
考虑对于坐标离散化。
然后我们考虑一个点会对于这个东西做出什么贡献,很明显就是对于正方形上端点在 \((x,y)\) 和 \((x+k-1,y+k-1)\) 的这个区域里选择的话,权值会多一个1.
那么完成了题意转化,每次选择一个区域区间加1,然后求最大权值的某个点的权值。
这是个扫描线的板子。
#include <bits/stdc++.h>
using std::pair;
using std::cin;
using std::cout;
const int N = 4e5 + 10;
pair<int, int> p[N];
int n, k, cnt, t[N];
struct LINE {
int l, r, h, ty;
LINE(int L = 0, int R = 0, int H = 0, int TY = 0) : l(L), r(R), h(H), ty(TY) {}
bool operator < (const LINE &a) const {
return this->h != a.h ? this->h < a.h : this->ty < a.ty;
}
} line[N];
struct Node {
int mx, tag;
} qs[N * 4];
inline int ls(int k) {
return k << 1;
}
inline int rs(int k) {
return k << 1 | 1;
}
inline void puttag(int k, int x) {
qs[k].mx += x;
qs[k].tag += x;
}
inline void down(int k) {
if (qs[k].tag) {
puttag(ls(k), qs[k].tag);
puttag(rs(k), qs[k].tag);
qs[k].tag = 0;
}
}
inline void maintain(int k) {
qs[k].mx = std::max(qs[ls(k)].mx, qs[rs(k)].mx);
}
void modify(int k, int l, int r, int ql, int qr, int x) {
if (ql <= l && r <= qr) {
return puttag(k, x);
}
down(k);
int mid = (l + r) >> 1;
if (ql <= mid) {
modify(ls(k), l, mid, ql, qr, x);
}
if (mid < qr) {
modify(rs(k), mid + 1, r, ql, qr, x);
}
maintain(k);
}
int main() {
freopen("frame.in", "r", stdin);
freopen("frame.out", "w", stdout);
std::ios::sync_with_stdio(0);
std::cin.tie(0);
cin >> n >> k;
for (int i = 1; i <= n; ++i) {
cin >> p[i].first >> p[i].second;
t[++cnt] = p[i].first;
t[++cnt] = p[i].second;
t[++cnt] = p[i].first + k - 1;
t[++cnt] = p[i].second + k - 1;
}
std::sort(t + 1, t + 1 + cnt);
cnt = std::unique(t + 1, t + 1 + cnt) - t - 1;
// t[1] - t[cnt]
// 离散化
int lcnt = 0;
for (int i = 1; i <= n; ++i) {
int L = std::lower_bound(t + 1, t + 1 + cnt, p[i].first) - t, R = std::lower_bound(t + 1, t + 1 + cnt, p[i].first + k - 1) - t, h1 = std::lower_bound(t + 1, t + 1 + cnt, p[i].second) - t, h2 = std::lower_bound(t + 1, t + 1 + cnt, p[i].second + k - 1) - t;
line[++lcnt] = LINE(L, R, h1, -1);
// 添加
line[++lcnt] = LINE(L, R, h2, 1);
// 删除
}
int ans = 0;
std::sort(line + 1, line + lcnt + 1);
for (int i = 1, j = 1; i <= lcnt; i = j) {
for (; j <= lcnt && line[j].h == line[i].h && line[j].ty == -1; ++j) {
modify(1, 1, cnt, line[j].l, line[j].r, -line[j].ty);
}
ans = std::max(ans, qs[1].mx);
for (; j <= lcnt && line[j].h == line[i].h && line[j].ty == 1; ++j) {
modify(1, 1, cnt, line[j].l, line[j].r, -line[j].ty);
}
}
cout << ans << '\n';
return 0;
}