题解 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
    评论
题目描述似乎缺失了关键信息,通常我会需要了解“P10780 食物”是什么具体的算法竞赛题目,它来自在线平台洛谷(Luogu),以及该题目的大致背景、条件和目标。洛谷食物(Food)可能是某种数据结构或算法问题,比如贪吃蛇、分配任务等。 然而,我可以给你提供一个通用的模板: **[洛谷 P10780 食物 - 题目解析]** 题目名称:P10780 食物(假设是关于食物分配或者饥饿游戏的问题) 链接:[插入实际题目链接] **背景:** 此题通常涉及动态规划或者搜索策略。场景可能是有n个参与者(选手或角色),每个都有特定的食物需求或者优先级,我们需要在有限的食物资源下合理分配。 **分析:** 1. **输入理解**:首先读入n个参与者的信息,包括每个人的需求量或优先级。 2. **状态定义**:可以定义dp[i][j]表示前i个人分配完成后剩余的食物能满足第j个人的最大程度。 3. **状态转移**:递推式可能涉及到选择当前人分配最多食物的版本,然后更新剩余的食物数。 4. **边界条件**:如果剩余食物不足以满足某人的需求,则考虑无法分配给他;如果没有食物,状态值设为0。 5. **优化策略**:可能需要对状态数组进行滚动更新,以减少空间复杂度。 **代码示例(伪代码或部分关键代码片段):** ```python # 假设函数分配_food(demand, remaining)计算分配给一个人后剩余的食物 def solve(foods): dp = [[0 for _ in range(max_demand + 1)] for _ in range(n)] dp = foods[:] # 从第一个到最后一个参与者处理 for i in range(1, n): for j in range(1, max_demand + 1): if dp[i-1][j] > 0: dp[i][j] = max(dp[i][j], dp[i-1][j] - foods[i]) dp[i][j] = max(dp[i][j], distribute_food_to(i, dp[i-1][j])) return dp[n-1][max_demand] ``` **相关问题--:** 1. 这道题是如何运用动态规划的? 2. 如果有优先级限制,应该如何调整代码? 3. 怎样设计搜索策略来解决类似问题?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值