[笛卡尔树] [CEOI2020]花式围栏

\(\texttt{link}\)

题解区似乎没有笛卡尔树写法?

考虑按 \(h_i\) 的大小建一棵笛卡尔树, \(h_i\) 越小离根越近。

树上的点 \(u\) 代表的是高为 \(h_u\) ,宽为 \(\sum\limits_{v\in \texttt{子树u}}w_v\) 的矩形,但是如果直接对每个点计算并求和会算重。

因此,对于每个点,我们只计算经过上边界 \(> h_{fa}\) 的矩形,这样就能做到不重不漏。

计算方式并不是这题的重点,略去。(绝对不是懒得写

\(\texttt{Code:}\)

#include <bits/stdc++.h>
#define ll long long
#define pb push_back
#define mp make_pair
#define fi first
#define se second

using namespace std;

template<typename T> inline void read(T &x) {
	x = 0; int f = 0; char c = getchar();
	while (!isdigit(c)) f |= c == '-', c = getchar();
	while (isdigit(c)) x = x * 10 + (c ^ 48), c = getchar();
	x = f ? -x : x;
}

template<typename T> inline void writeln(T x) {
	if (x == 0) {putchar('0'); putchar('\n'); return;}
	int w = 0, p[21];
	while (x) p[++w] = x % 10, x /= 10;
	while (w) putchar(p[w--] + '0');
	putchar('\n');
}

const int cmd = 1e9 + 7;
const int N = 1e5 + 5;

int fpow(int a, int b) {
	int res = 1;
	for (; b; b >>= 1, a = 1ll * a * a % cmd)
		if (b & 1) res = 1ll * res * a % cmd;
	return res;
}

int add(int a, int b) {a += b; return a < cmd ? a : a - cmd;}

int sub(int a, int b) {a -= b; return a < 0 ? a + cmd : a;}

int n, h[N], stk[N], top, ls[N], rs[N], ans, w[N], inv2 = fpow(2, cmd - 2);

void dfs(int u, int fa) {
	if (ls[u]) dfs(ls[u], u), w[u] = add(w[u], w[ls[u]]);
	if (rs[u]) dfs(rs[u], u), w[u] = add(w[u], w[rs[u]]);
	int cur = 1ll * (h[u] + h[fa] + 1) * (h[u] - h[fa]) / 2 % cmd;
	cur = 1ll * cur * (1 + w[u]) % cmd * w[u] % cmd * inv2 % cmd;
	ans = add(ans, cur);
}

int main() {
	#ifdef LOCAL
	freopen("a.in", "r", stdin);
	freopen("a.out", "w", stdout);
	#endif
	read(n);
	for (int i = 1; i <= n; i++) read(h[i]);
	for (int i = 1; i <= n; i++) read(w[i]);
	for (int i = 1; i <= n; i++) {
		while (top && h[stk[top]] > h[i]) ls[i] = stk[top--];
		if (top) rs[stk[top]] = i;
		stk[++top] = i;
	}
	dfs(stk[1], 0);
	printf("%d", ans);
	return 0;
}
上一篇:git使用方法及常用命令


下一篇:antd表单-单页面多表单提交功能