【洛谷5268】[SNOI2017] 一个简单的询问(莫队)

点此看题面

  • 给定一个长度为(n)的序列。
  • (q)次询问,每次给定两个区间,询问有多少种方式从这两个区间中选出两个相同的数。
  • (n,qle5 imes10^4)

简单容斥+莫队

考虑题目要求的是(F(l_1,r_1,l_2,r_2)),那么显然可以对它做一个容斥,把它裂成四个区间,得到:

[F(1,r_1,1,r_2)-F(1,l_1-1,1,r_2)-F(1,r_1,1,l_2-1)+F(1,l_1-1,1,l_2-1) ]

然后就是对于莫队不要有太局限的想法,认为它只能处理区间的问题,实际上这种两个指针移动的问题都可以用莫队解决。

学过莫队的人应该仔细想想就能明白这道题该怎么做吧。

代码:(O(nsqrt n))

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 50000
#define LL long long
using namespace std;
int n,m,a[N+5],sz,bl[N+5],c1[N+5],c2[N+5];LL ans[N+5];
struct Q
{
	int op,p,a,b;I Q(CI t=0,CI i=0,CI x=0,CI y=0):op(t),p(i),a(x),b(y){}
	I bool operator < (Con Q& o) Con {return bl[a]^bl[o.a]?a<o.a:(bl[a]&1?b<o.b:b>o.b);}
}q[4*N+5];
namespace FastIO
{
	#define FS 100000
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
	int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
	Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('
');}
}using namespace FastIO;
int main()
{
	RI i,l1,r1,l2,r2;for(read(n),sz=sqrt(n),i=1;i<=n;++i) read(a[i]),bl[i]=(i-1)/sz+1;
	for(read(m),i=1;i<=m;++i) read(l1,r1,l2,r2),q[4*i-3]=Q(1,i,r1,r2),
		q[4*i-2]=Q(-1,i,r1,l2-1),q[4*i-1]=Q(-1,i,l1-1,r2),q[4*i]=Q(1,i,l1-1,l2-1);//容斥,裂成四个区间
	RI A=0,B=0;LL t=0;for(sort(q+1,q+4*m+1),i=1;i<=4*m;ans[q[i].p]+=q[i].op*t,++i)
	{
		W(A<q[i].a) ++c1[a[++A]],t+=c2[a[A]];W(A>q[i].a) --c1[a[A]],t-=c2[a[A--]];//移动A指针
		W(B<q[i].b) ++c2[a[++B]],t+=c1[a[B]];W(B>q[i].b) --c2[a[B]],t-=c1[a[B--]];//移动B指针
	}
	for(i=1;i<=m;++i) writeln(ans[i]);return clear(),0;
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu5268.html