一、题目
题目说的很不清楚,我再来解释一下:
有一个 n × n n\times n n×n 的矩阵,一开始有人在上面标记了 n n n个点(不重行,不重列),询问给定一个矩形,问有多少选两个点组成的矩形与给定的矩形相交(挨着也行)。
二、解法
正难则反,有一种计算方法就是用所有矩形减去不相交的矩形, x x x个点能生成的矩形数为 x ( x − 1 ) 2 \frac{x(x-1)}{2} 2x(x−1) 。
可以先减去上下左右四个方向所能生成的矩形数量,那这样会不会算重呢?答案就是我们减多了,因为左下角,右下角,左上角,右上角所能生成的矩形数量都被算了两遍,所以要加回去。
本题范围很大,但是点很不多,可以用主席树来维护前缀和,时间复杂度&空间复杂度 O ( n log n ) O(n\log n) O(nlogn)。
#include <cstdio>
#define LL long long
const int N = 200005;
const int M = 30*N;
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,q,cnt,rt[N],sum[M],ls[M],rs[M];
int ins(int x,int l,int r,int id)
{
int t=++cnt;
sum[t]=sum[x]+1;ls[t]=ls[x];rs[t]=rs[x];
if(l==r) return t;
int mid=(l+r)>>1;
if(mid>=id)
ls[t]=ins(ls[x],l,mid,id);
else
rs[t]=ins(rs[x],mid+1,r,id);
return t;
}
int ask(int x,int y,int l,int r,int L,int R)
{
if(l>R || L>r) return 0;
if(L<=l && r<=R) return sum[y]-sum[x];
int mid=(l+r)>>1;
return ask(ls[x],ls[y],l,mid,L,R)+ask(rs[x],rs[y],mid+1,r,L,R);
}
LL cal(int x)
{
return 1ll*x*(x-1)/2;
}
int main()
{
n=read(),q=read();
for(int i=1;i<=n;i++)
{
int x=read();
rt[i]=ins(rt[i-1],1,n,x);
}
while(q--)
{
int a=read(),b=read(),c=read(),d=read();
LL res=cal(n)-cal(a-1)-cal(b-1)-cal(n-c)-cal(n-d);
if(b>1)
{
res+=cal(ask(rt[0],rt[a-1],1,n,1,b-1));
res+=cal(ask(rt[c],rt[n],1,n,1,b-1));
}
if(d<n)
{
res+=cal(ask(rt[0],rt[a-1],1,n,d+1,n));
res+=cal(ask(rt[c],rt[n],1,n,d+1,n));
}
printf("%lld\n",res);
}
}