题解【洛谷P2221】[HAOI2012]高速公路

题面

我们需要一棵支持区间加、区间询问所有子区间和的线段树。

对于询问,假设询问区间是 \([l,r]\),答案即为:

\[\dfrac{\sum\limits_{i=l}^r\sum\limits_{j=i}^r dis_{i,j}}{\binom{r-l+1}{2}} \]

分母可以直接 \(\mathcal{O}(1)\) 算;

对于分子,我们发现点之间的距离不好用线段树维护,于是我们把边转成点,然后考虑计算每一条边对答案的贡献:

\[\begin{aligned}\sum_{i=l}^r v_i(r-i+1)(i-l+1) &= \sum\limits_{i=l}^r v_i(ri-lr+r-i^2+il-i+i-l+1) \\ &= \sum\limits_{i=l}^r -i^2v_i+iv_i(r+l)+v_i(-lr+r-l+1)\end{aligned}\]

\(s_1=\sum v_i\)\(s_2=\sum iv_i\)\(s_3=\sum i^2v_i\)

答案即为:

\[\begin{aligned} -s3+s_2(r+l)+s_1(-lr+r-l+1)\end{aligned} \]

可以开 \(3\) 棵线段树解决。

然后考虑怎么修改。

\(s_1\):直接区间修改即可。

\(s_2\)\(\sum i(v_i+k)=\sum iv_i+ik\),可以维护一个前缀和 \(s_4\) 记录 \(\sum i\)

\(s_3\)\(\sum i^2(v_i+k)=\sum i^2v_i+i^2k\),维护一个前缀和 \(s_5\) 记录 \(\sum i^2\)

推荐练习:[SDOI2017]相关分析

代码:

#include <bits/stdc++.h>
#define DEBUG fprintf(stderr, "Passing [%s] line %d\n", __FUNCTION__, __LINE__)
#define File(x) freopen(x".in","r",stdin); freopen(x".out","w",stdout)
#define int long long

using namespace std;

typedef long long LL;
typedef pair <int, int> PII;
typedef pair <int, PII> PIII;

template <typename T>
inline T gi()
{
	T f = 1, x = 0; char c = getchar();
	while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
	while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
	return f * x;
}

const int INF = 0x3f3f3f3f, N = 100003, M = N << 1;

int n, m;
LL sum[4][N << 2], lz[4][N << 2];
LL pr[3][N << 2];

inline int ls(int p) {return p << 1;}
inline int rs(int p) {return p << 1 | 1;}

inline void pushup(int p)
{
	for (int i = 1; i <= 3; i+=1) sum[i][p] = sum[i][ls(p)] + sum[i][rs(p)];
}

inline void mark(int l, int r, int val, int p)
{
	sum[1][p] += 1ll * val * (r - l + 1),
		sum[2][p] += 1ll * val * (pr[1][r] - pr[1][l - 1]),
		sum[3][p] += 1ll * val * (pr[2][r] - pr[2][l - 1]);
	lz[1][p] += val, lz[2][p] += val, lz[3][p] += val;
}

inline void pushdown(int l, int r, int p)
{
	int mid = (l + r) >> 1;
	if (lz[1][p])
	{
		sum[1][ls(p)] += 1ll * lz[1][p] * (mid - l + 1),
			sum[1][rs(p)] += 1ll * lz[1][p] * (r - mid);
		lz[1][ls(p)] += lz[1][p],
			lz[1][rs(p)] += lz[1][p];
		lz[1][p] = 0;
	}
	if (lz[2][p])
	{
		sum[2][ls(p)] += 1ll * lz[2][p] * (pr[1][mid] - pr[1][l - 1]),
			sum[2][rs(p)] += 1ll * lz[2][p] * (pr[1][r] - pr[1][mid]);
		lz[2][ls(p)] += lz[2][p],
			lz[2][rs(p)] += lz[2][p];
		lz[2][p] = 0;
	}
	if (lz[3][p])
	{
		sum[3][ls(p)] += 1ll * lz[3][p] * (pr[2][mid] - pr[2][l - 1]),
			sum[3][rs(p)] += 1ll * lz[3][p] * (pr[2][r] - pr[2][mid]);
		lz[3][ls(p)] += lz[3][p],
			lz[3][rs(p)] += lz[3][p];
		lz[3][p] = 0;
	}
}

void modify(int ql, int qr, int val, int l, int r, int p)
{
	if (ql <= l && r <= qr)
	{
		mark(l, r, val, p);
		return;
	}
	pushdown(l, r, p);
	int mid = (l + r) >> 1;
	if (ql <= mid) modify(ql, qr, val, l, mid, ls(p));
	if (qr > mid) modify(ql, qr, val, mid + 1, r, rs(p));
	pushup(p);
}

LL getans(int id, int ql, int qr, int l, int r, int p)
{
	if (ql <= l && r <= qr) return sum[id][p];
	pushdown(l, r, p);
	int mid = (l + r) >> 1;
	LL sum = 0;
	if (ql <= mid) sum += getans(id, ql, qr, l, mid, ls(p));
	if (qr > mid) sum += getans(id, ql, qr, mid + 1, r, rs(p));
	return sum;
}

inline LL query(int l, int r)
{
	LL s1 = getans(1, l, r, 1, n - 1, 1), s2 = getans(2, l, r, 1, n - 1, 1), s3 = getans(3, l, r, 1, n - 1, 1);
	return s2 * (l + r) + s1 * (r + 1 - l - l * r) - s3;
}

signed main()
{
	//File("");
	n = gi <int> (), m = gi <int> ();
	for (int i = 1; i <= n; i+=1)
		pr[1][i] = pr[1][i - 1] + i, pr[2][i] = pr[2][i - 1] + 1ll * i * i;
	while (m--)
	{
		char op[3];
		scanf("%s", op);
		int l = gi <int> (), r = gi <int> () - 1, v;
		if (op[0] == 'C')
		{
			v = gi <int> ();
			modify(l, r, v, 1, n - 1, 1);
		}
		else
		{
			if (l > r) {puts("0/1"); continue;}
			LL len = 1ll * (r - l + 1) * (r - l + 2) / 2;
			LL ans = query(l, r);
			LL G = __gcd(ans, len);
			ans /= G, len /= G;
			printf("%lld/%lld\n", ans, len);
		}
	}
	return 0;
}
posted @ 2020-09-05 22:17  csxsi  阅读(144)  评论(0编辑  收藏  举报