BZOJ 5016: [Snoi2017]一个简单的询问 莫队算法

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

Time Limit: 30 Sec  Memory Limit: 512 MB
Submit: 193  Solved: 147
[Submit][Status][Discuss]

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
注意:答案有可能超过int的最大值

Output

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

Sample Input

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

Sample Output

4
1


看了两个乘到一起觉得莫队不行 就狂想数据结构 最后没做出来

这个应该是一种降低运算级从而简化运算的思想

具体来讲

定义 s(i)=get(1,i,x) 令 l1--,l2--

则 原式= sigma (s(r1)-s(l1))*(s(r2)-s(l2))

展开之后 可以化成四个平方的和

sigma ((s(r1)−s(l2))^2+(s(r2)−s(l1))^2−(s(l2)−s(l1))^2−(s(r2)−s(r1))^2)/2

之后就可以莫队了


#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<string>
#include<bitset>
#include<queue>
#include<map>
#include<set>
using namespace std;

typedef double db;
typedef long long ll;

inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void print(ll x)
{if(x<0)putchar('-'),x=-x;if(x>=10)print(x/10);putchar(x%10+'0');}

const int N=50100;

int bel[N];

struct query
{
	int l,r,pos,opt;
	
	friend bool operator <(const query &x,const query &y)
	{return bel[x.l]==bel[y.l] ? x.r<y.r : bel[x.l]<bel[y.l];}
	
}q[N<<2];

int n,Q,tot;
int a[N];
ll res,ans[N];

int num[N];

inline void modify(int x,int opt)
{
	res+=1+(opt*num[x]<<1);
	num[x]+=opt;
}

void solve()
{
	sort(q+1,q+1+tot);
	register int i,l(1),r(0);
	for(i=1;i<=tot;++i)
	{
		while(r<q[i].r) r++,modify(a[r],1);
		while(l>q[i].l) l--,modify(a[l],1);
		while(r>q[i].r) modify(a[r],-1),r--;
		while(l<q[i].l) modify(a[l],-1),l++;
		ans[q[i].pos]+=q[i].opt*res;
	}
}

int main()
{
	n=read();
	register int i,j,l1(1),l2,r1,r2;
	for(i=1;i<=n;++i) a[i]=read();
	int block(floor(sqrt(n)));
	for(i=0,j=1;i<=n;++i,++j)
	{
		bel[i]=l1;
		if(j==block) j=0,l1++;
	}
	Q=read();
	for(i=1;i<=Q;++i)
	{
		l1=read()-1;r1=read();
		l2=read()-1;r2=read();
		q[++tot]=(query){min(l1,r2)+1,max(l1,r2),i,1};
		q[++tot]=(query){min(r1,l2)+1,max(r1,l2),i,1};
		q[++tot]=(query){min(l1,l2)+1,max(l1,l2),i,-1};
		q[++tot]=(query){min(r1,r2)+1,max(r1,r2),i,-1};
	}
	solve();
	for(i=1;i<=Q;++i)
		print(ans[i]>>1),putchar('\n');
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值