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;
}
posted @ 2021-10-03 12:31  siriehn_nx  阅读(70)  评论(0编辑  收藏  举报