【bzoj5016】[Snoi2017]一个简单的询问 莫队算法

题目描述

给你一个长度为N的序列ai,1≤i≤N和q组询问,每组询问读入l1,r1,l2,r2,需输出

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

输入

第一行,一个数字N,表示序列长度。

第二行,N个数字,表示a1~aN

第三行,一个数字Q,表示询问个数。

第4~Q+3行,每行四个数字l1,r1,l2,r2,表示询问。

N,Q≤50000

N1≤ai≤N

1≤l1≤r1≤N

1≤l2≤r2≤N

注意:答案有可能超过int的最大值

输出

对于每组询问,输出一行一个数字,表示答案

样例输入

5
1 1 1 1 1
2
1 2 3 4
1 1 4 4

样例输出

4
1



题解

莫队算法

(为了方便,以下使用$S_x(l,r)$代替$get(l,r,x)$)

题目一眼莫队,不过由于一个询问有4个参数,不能直接处理。

考虑将询问拆成前缀相减的形式,即:

$\ \ \ \ \sum\limits_xS_x(l_1,r_1)·S_x(l_2,r_2)\\=\sum\limits_{x}(S_x(1,r_1)-S_x(1,l_1-1))·(S_x(1,r_2)-S_x(1,l_2-1))\\=\sum\limits_{x}(S_x(1,r_1)·S_x(1,r_2)-S_x(1,l_1-1)·S_x(1,r_2)--S_x(1,r_1)·S_x(1,l_2-1)+S_x(1,l_1-1)·S_x(1,l_2-1))\\=Q(r_1,r_2)-Q(l_1-1,r_2)-Q(r_1,l_2-1)+Q(l_1-1,l_2-1)$

其中:

$Q(a,b)=\sum\limits_{x}S_x(1,a)·S_x(1,b)$

于是就可以把每个询问拆成4个,使用莫队算法分别计算对每个答案的贡献即可。

注意当$a$或$b$中某一个为0时的情况需要过滤掉,否则会加入不存在的位置导致挂掉。

时间复杂度$O(n\sqrt{4m})$

#include <cmath>
#include <cstdio>
#include <algorithm>
#define N 50010
using namespace std;
typedef long long ll;
int si , v[N] , cl[N] , cr[N] , tot;
ll ans[N];
struct data
{
	int l , r , flag , id;
	data() {}
	data(int a , int b , int c , int d) {l = min(a , b) , r = max(a , b) , flag = c , id = d;}
	bool operator<(const data &a)const {return (l - 1) / si == (a.l - 1) / si ? r < a.r : (l - 1) / si < (a.l - 1) / si;}
}a[N << 2];
int main()
{
	int n , m , i , x1 , y1 , x2 , y2 , lp = 0 , rp = 0;
	ll now = 0;
	scanf("%d" , &n) , si = (int)sqrt(n);
	for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &v[i]);
	scanf("%d" , &m);
	for(i = 1 ; i <= m ; i ++ )
	{
		scanf("%d%d%d%d" , &x1 , &y1 , &x2 , &y2) , a[++tot] = data(y1 , y2 , 1 , i);
		if(x1 > 1) a[++tot] = data(x1 - 1 , y2 , -1 , i);
		if(x2 > 1) a[++tot] = data(y1 , x2 - 1 , -1 , i);
		if(x1 > 1 && x2 > 1) a[++tot] = data(x1 - 1 , x2 - 1 , 1 , i);
	}
	sort(a + 1 , a + tot + 1);
	for(i = 1 ; i <= tot ; i ++ )
	{
		while(lp < a[i].l) lp ++ , now += cr[v[lp]] , cl[v[lp]] ++ ;
		while(rp < a[i].r) rp ++ , now += cl[v[rp]] , cr[v[rp]] ++ ;
		while(lp > a[i].l) cl[v[lp]] -- , now -= cr[v[lp]] , lp -- ;
		while(rp > a[i].r) cr[v[rp]] -- , now -= cl[v[rp]] , rp -- ;
		ans[a[i].id] += a[i].flag * now;
	}
	for(i = 1 ; i <= m ; i ++ ) printf("%lld\n" , ans[i]);
	return 0;
}
时间: 2024-10-09 09:58:02

【bzoj5016】[Snoi2017]一个简单的询问 莫队算法的相关文章

【BZOJ5016】[Snoi2017]一个简单的询问 莫队

[BZOJ5016][Snoi2017]一个简单的询问 Description 给你一个长度为N的序列ai,1≤i≤N和q组询问,每组询问读入l1,r1,l2,r2,需输出 get(l,r,x)表示计算区间[l,r]中,数字x出现了多少次. Input 第一行,一个数字N,表示序列长度. 第二行,N个数字,表示a1-aN 第三行,一个数字Q,表示询问个数. 第4-Q+3行,每行四个数字l1,r1,l2,r2,表示询问. N,Q≤50000 N1≤ai≤N 1≤l1≤r1≤N 1≤l2≤r2≤N

bzoj5016 [Snoi2017]一个简单的询问

传送门 分析 我们发现可以通过容斥得到Ans = sum(1,R1,1,R2) - sum(1,R1,1,L2-1) - sum(1,L1-1,1,R2) + sum(1,L1-1,L2-1) 于是我们可以吧一个询问分成4部分 然后进行莫队即可 代码 #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<algorithm> #include

bzoj 5016: [Snoi2017]一个简单的询问

Description 给你一个长度为N的序列ai,1≤i≤N和q组询问,每组询问读入l1,r1,l2,r2,需输出 get(l,r,x)表示计算区间[l,r]中,数字x出现了多少次. Input 第一行,一个数字N,表示序列长度.第二行,N个数字,表示a1-aN第三行,一个数字Q,表示询问个数.第4-Q+3行,每行四个数字l1,r1,l2,r2,表示询问.N,Q≤50000N1≤ai≤N1≤l1≤r1≤N1≤l2≤r2≤N注意:答案有可能超过int的最大值Output 对于每组询问,输出一行一

[Snoi2017]一个简单的询问

数据范围很容易让人想到莫队算法 但是对于每次询问有\(l_1,r_1,l_2,r_2\)四个参数 很不方便维护 所以可以将询问差分 \(get(l,r,x)=get(1,r,x)-get(1,l-1,x)\) \(get(l_1,r_1,x)*get(l_2,r_2,x)\) \(=get(1,r_1,x)*get(1,r_2,x)\) \(-get(1,l_1-1,x)*get(1,r_2,x)\) \(-get(1,r_1,x)*get(1,l_2-1,x)\) \(+get(1,l_1-1

【LOJ2254】SNOI2017一个简单的询问

莫队,每次询问的是两个区间,就把区间拆开,分开来算就好了. 借鉴了rank1大佬的玄学排询问的姿势. #include<bits/stdc++.h> #define N 50010 typedef long long ll; using namespace std; inline int read(){ int f=1,x=0;char ch; do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9'); do{x=x*10+ch-

BZOJ 3781 小B的询问 莫队算法

题目大意:一共有M个询问,每个询问给定一个区间[L..R],求Sigma(c(i)^2)的值,其中i的值从1到K,其中c(i)表示数字i在[L..R]中的重复次数. 思路:莫队走起. CODE: #include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX 50010 using namespac

P2709 小B的询问 莫队算法

题意:小B有一个序列,包含N个1~K之间的整数.他一共有M个询问,每个询问给定一个区间[L..R],求Sigma(c(i)^2)的值,其中i的值从1到K,其中c(i)表示数字i在[L..R]中的重复次数.小B请你帮助他回答询问. 就是求区间不同数字个数的平方和 注意平方数可以拆开从1-n 递推... #include<bits/stdc++.h> using namespace std; //input by bxd #define rep(i,a,b) for(int i=(a);i<

【莫队算法】

·通过排序巧妙优化复杂度,NOIP前的最后一丝宁静. ·目前的题型概括为三种:普通莫队,树形莫队以及带修莫队. 若谈及入门,那么BZOJ2038的美妙袜子一题堪称经典. [例题一]袜子 ·题目大意:      进行区间询问[l,r],输出该区间内随机抽两次抽到相同颜色袜子的概率. ·分析:      首先考虑对于一个长度为n区间内的答案如何求解.题目要求Ans使用最简分数表示:那么分母就是n*n(表示两两袜子之间的随机组合),分子是一个累加和,累加的内容是该区间内每种颜色i出现次数sum[i]的

(普通的)莫队算法简单介绍

莫队算法(由莫涛发明的)是一种离线的暴力算法(至少我这么认为).使用莫队算法的条件是,知道一个区间[l, r]的结果,那么也可以快速知道[l + 1, r],[l - 1, r], [l, r - 1], [l, r + 1]这四个区间的结果.于是可以想到,直接通过这样转移来解决一些问题.当然有些出题人机智,故意卡这种暴力,让你从头跑到尾然后从尾跑到头,于是时间复杂度高达O(n2) 而莫队算法就是通过改变处理询问的顺序来降低时间复杂度. 比如说现在知道一个区间[l1, r1],又要转移到[l2,