题解 P5268 [SNOI2017] 一个简单的询问

描述

题目传送门

给定一个长度为 n n n 的序列 a a a q q q 次询问。每次询问给定 l 1 , r 1 , l 2 , r 2 l_1,r_1,l_2,r_2 l1,r1,l2,r2,求 ∑ x = 0 ∞ get ( l 1 , r 1 , x ) ⋅ get ( l 2 , r 2 , x ) \sum\limits_{x=0}^\infty \text{get}(l_1,r_1,x)\cdot\text{get}(l_2,r_2,x) x=0get(l1,r1,x)get(l2,r2,x)

get ( l , r , x ) \text{get}(l,r,x) get(l,r,x) 表示计算区间 [ l , r ] [l,r] [l,r] 中,数字 x x x 出现了多少次。

思路

考虑容斥,即 get ( l , r , x ) = get ( 1 , r , x ) − get ( 1 , l − 1 , x ) \text{get}(l,r,x)=\text{get}(1,r,x)-\text{get}(1,l-1,x) get(l,r,x)=get(1,r,x)get(1,l1,x)

再来化简式子:

∑ x = 0 ∞ get ( l 1 , r 1 , x ) ⋅ get ( l 2 , r 2 , x ) = ∑ x = 0 ∞ [ get ( 1 , r 1 , x ) − get ( 1 , l 1 − 1 , x ) ] ⋅ [ get ( 1 , r 2 , x ) − get ( 1 , l 2 − 1 , x ) ] \sum\limits_{x=0}^\infty \text{get}(l_1,r_1,x)\cdot\text{get}(l_2,r_2,x) =\sum\limits_{x=0}^\infty [\text{get}(1,r_1,x)-\text{get}(1,l_1-1,x)]\cdot[\text{get}(1,r_2,x)-\text{get}(1,l_2-1,x)] x=0get(l1,r1,x)get(l2,r2,x)=x=0[get(1,r1,x)get(1,l11,x)][get(1,r2,x)get(1,l21,x)]

g ( p ) = get ( 1 , p , x ) \text{g}(p)=\text{get}(1,p,x) g(p)=get(1,p,x),即 1 ∼ p 1\sim p 1p x x x 的出现次数。

展开,得:

∑ x = 0 ∞ g ( r 1 ) ⋅ g ( r 2 ) − ∑ x = 0 ∞ g ( r 1 ) ⋅ g ( l 2 − 1 ) − ∑ x = 0 ∞ g ( l 1 − 1 ) ⋅ g ( r 2 ) + ∑ x = 0 ∞ g ( l 1 − 1 ) ⋅ g ( l 2 − 1 ) \sum\limits_{x=0}^\infty \text{g}(r_1)\cdot\text{g}(r_2)-\sum\limits_{x=0}^\infty\text{g}(r_1)\cdot\text{g}(l_2-1)- \sum\limits_{x=0}^\infty\text{g}(l_1-1)\cdot\text{g}(r_2)+ \sum\limits_{x=0}^\infty\text{g}(l_1-1)\cdot\text{g}(l_2-1) x=0g(r1)g(r2)x=0g(r1)g(l21)x=0g(l11)g(r2)+x=0g(l11)g(l21)

这样就把一次询问强行拆成了 4 4 4 次询问, 4 4 4 次询问的和(差)即为答案。

记二元组 ( l , r ) (l,r) (l,r) 表示一小次询问 ± ∑ x = 0 ∞ g ( l ) ⋅ g ( r ) \pm\sum\limits_{x=0}^\infty\text{g}(l)\cdot\text{g}(r) ±x=0g(l)g(r),即所有数在 1 ∼ l 1\sim l 1l 的出现次数 × \times × 1 ∼ r 1\sim r 1r 的出现次数。

发现从 ( l , r ) (l,r) (l,r) 转移到 ( l ′ , r ′ ) (l',r') (l,r) 的暴力转移的时间复杂度是 O ( abs ( l ′ − l ) + abs ( r ′ − r ) ) \mathcal{O}(\text{abs}(l'-l)+\text{abs}(r'-r)) O(abs(ll)+abs(rr)),可以直接莫队。

代码

#include <bits/stdc++.h>
using namespace std;
#define re register
#define int unsigned
#define MAXN 100010
int n, k, m, a[MAXN], len, sum, bel[MAXN], geta[MAXN], getl[MAXN], getr[MAXN], ans[MAXN];
struct ques {
	int i, l, r, z;
	ques() {}
	ques(const int &a, const int &b, const int &c, const int &d) {
		i = a, l = b, r = c, z = d;
	}
} q[MAXN << 2];
inline bool cmp(const ques &a, const ques &b) {
	if (bel[a.l] != bel[b.l]) return a.l < b.l;
	if (bel[a.l] & 1) return a.r < b.r;
	else return a.r > b.r;
}
inline void update_r(const int &c) {
	sum -= geta[c]; geta[c] += getl[c]; getr[c]++; sum += geta[c];
}
inline void update_l(const int &c) {
	sum -= geta[c]; geta[c] += getr[c]; getl[c]++; sum += geta[c];
}
inline void remove_r(const int &c) {
	sum -= geta[c]; geta[c] -= getl[c]; getr[c]--; sum += geta[c];
}
inline void remove_l(const int &c) {
	sum -= geta[c]; geta[c] -= getr[c]; getl[c]--; sum += geta[c];
}
signed main() {
	cin >> n;
	for (register int i = 1; i <= n; i++) cin >> a[i];
	cin >> k;
	for (register int i = 1; i <= k; i++) {
		int l1, r1, l2, r2;
		cin >> l1 >> r1 >> l2 >> r2;
		q[++m] = ques(i, r1, r2, 1), q[++m] = ques(i, r1, l2 - 1, -1), q[++m] = ques(i, l1 - 1, r2, -1), q[++m] = ques(i, l1 - 1, l2 - 1, 1);
	}
	len = n / sqrt(n * 2 / 3);
	for (register int i = 1; i <= n; i++) bel[i] = (i - 1) / len + 1;
	sort(q + 1, q + m + 1, cmp);
	int l = 0, r = 0;
	for (register int i = 1; i <= m; i++) {
		for (register int j = l + 1; j <= q[i].l; j++) update_l(a[j]);
		for (register int j = l; j > q[i].l; j--) remove_l(a[j]);
		for (register int j = r + 1; j <= q[i].r; j++) update_r(a[j]);
		for (register int j = r; j > q[i].r; j--) remove_r(a[j]);
		l = q[i].l, r = q[i].r;
		ans[q[i].i] += q[i].z * sum;
	}
	for (register int i = 1; i <= k; i++) cout << ans[i] << endl;
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值