版权声明:存在错误或者不清楚的地方还望指出 https://blog.csdn.net/hypHuangYanPing/article/details/82962375
/*
HDU 5213 Lucky 离线莫队+容斥;
链接:http://acm.hdu.edu.cn/showproblem.php?pid=5213
题意:所给两个区间各选一个数的和为k的二元组的数量;
分析:直接对两个区间进行莫队离线 显然是不可行的 移动的次数较多-->TLE;
考虑将两个区间进行合并 容斥原理
假设所选区间为 f(l,r) :表示区间[l,r]满足题意的二元组的数量;
G((l,r),(u,v)):表示对应两段区间满足条件的数量;
对于所给最大区间 则:f(l,v)=f(l,u-1)+f(r+1,v)-f(r+1,u-1)+G((l,r),(u,v));
确认每个区间相邻的状态,最后就是简单的莫队了,直接套用即可......
当然还有在线分块的写法 解法也比较自然;
*/
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=4e5+7;
int n,m,k,q,blo,pos[maxn];
int s[maxn],num[maxn];
int val[maxn];
struct node{
int l,r,u,v,id,flag,num;
bool operator <(const node &a)const{
return pos[l]<pos[a.l]||(pos[l]==pos[a.l]&&r<a.r);
}
}a[maxn];
int main(){
while(~scanf("%d %d",&n,&k)){
blo=(int)sqrt(n*1.0);
for(int i=1;i<=n;i++) scanf("%d",&s[i]),pos[i]=(i-1)/blo+1;
scanf("%d",&q);
int cnt=1;
for(int i=1;i<=q;i++) {
int l,r,u,v;scanf("%d %d %d %d",&l,&r,&u,&v);
a[cnt].l=l,a[cnt].r=v,a[cnt].num=1,a[cnt].id=i,cnt++;
a[cnt].l=r+1,a[cnt].r=u-1,a[cnt].num=2,a[cnt].id=i,cnt++;
a[cnt].l=l,a[cnt].r=u-1,a[cnt].num=3,a[cnt].id=i,cnt++;
a[cnt].l=r+1,a[cnt].r=v,a[cnt].num=4,a[cnt].id=i,cnt++;
}
sort(a+1,a+cnt);
memset(val,0,sizeof(val));
memset(num,0,sizeof(num));
int l=1,r=0,ret=0;
for(int i=1;i<cnt;i++){
if(a[i].l>a[i].r) continue;
while(r<a[i].r){
r++;
num[s[r]]++;
ret+=(k-s[r])>=0?num[k-s[r]]:0;
}
while(r>a[i].r){
ret-=(k-s[r])>=0?num[k-s[r]]:0;
num[s[r]]--;
r--;
}
while(l>a[i].l){
l--;
num[s[l]]++;
ret+=(k-s[l])>=0?num[k-s[l]]:0;
}
while(l<a[i].l){
ret-=(k-s[l])>=0?num[k-s[l]]:0;
num[s[l]]--;
l++;
}
if(a[i].num<=2) val[a[i].id]+=ret;
else val[a[i].id]-=ret;
}
for(int i=1;i<=q;i++) printf("%d\n",val[i]);
}
return 0;
}
/*
7
3
0 1 2 1 2 3 2
3
1 2 3 7
1 2 3 5
3 5 6 7
4 2 1
5
3
1 2 1 2 3
1
1 2 3 5
*/