CLYZ-NOIP十連測 Day2
阿新 • • 發佈:2021-10-03
掛100分
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;
}