https://www.cnblogs.com/ouuan/p/2DMoDui.html
输入一个n*m的矩阵,矩阵的每一个元素都是一个整数,然后有q个询问,每次询问一个子矩阵的权值。矩阵的权值是这样定义的,对于一个整数x,如果它在该矩阵中出现了p次,那么它给该矩阵的权值就贡献p2。
Input
第一行两个整数n,m表示矩阵的规模。
接下来n行每行m个整数,表示这个矩阵的每个元素。
再下来一行一个整数q,表示询问个数。
接下来q行每行四个正整数x1,y1,x2,y2,询问以第x1行第y1列和第x2行第y2列的连线为对角线的子矩阵的权值。
Output
输出q行每行一个整数回答对应询问。
Sample Input
3 4
1 3 2 1
1 3 2 4
1 2 3 4
8
1 2 2 1
1 1 2 1
1 1 3 4
1 1 1 1
2 2 3 3
3 4 2 2
1 3 3 1
2 4 3 4
Sample Output
8
4
38
1
8
12
27
4
HINT
对于全部数据
1<=n,m<=200
q<=100000
|矩阵元素大小|<=2*10^9
Sol:
二维莫队
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const int N=210;
const int M=100010;
void add(int x);
void del(int x);
int r,c,m,B,a[N][N],lsh[N*N],tot,cnt[N*N],ans,out[M];
struct Query
{
int x,y,xx,yy,id;
bool operator<(Query& b)
{
return x/B==b.x/B?(y/B==b.y/B?(xx/B==b.xx/B?yy<b.yy:xx<b.xx):y<b.y):x<b.x;
}
} q[M];
int main()
{
int i,j,x=1,y=1,xx=0,yy=0;
cin>>r>>c>>m;
B=pow(r*c,0.5)/pow(m,0.25)+1.0;
for (i=1;i<=r;++i)
{
for (j=1;j<=c;++j)
{
cin>>a[i][j];
lsh[tot++]=a[i][j]; //这题要离散化
}
}
sort(lsh,lsh+tot);
tot=unique(lsh,lsh+tot)-lsh;
for (i=1;i<=r;++i)
{
for (j=1;j<=c;++j)
{
a[i][j]=lower_bound(lsh,lsh+tot,a[i][j])-lsh;
}
}
for (i=0;i<m;++i)
{
cin>>q[i].x>>q[i].y>>q[i].xx>>q[i].yy;
q[i].id=i;
}
sort(q,q+m);
for (i=0;i<m;++i)
{
while (x>q[i].x)
{
--x;
for (j=y;j<=yy;++j)
{
add(a[x][j]);
}
}
while (xx<q[i].xx)
{
++xx;
for (j=y;j<=yy;++j)
{
add(a[xx][j]);
}
}
while (y>q[i].y)
{
--y;
for (j=x;j<=xx;++j)
{
add(a[j][y]);
}
}
while (yy<q[i].yy)
{
++yy;
for (j=x;j<=xx;++j)
{
add(a[j][yy]);
}
}
while (x<q[i].x)
{
for (j=y;j<=yy;++j)
{
del(a[x][j]);
}
++x;
}
while (xx>q[i].xx)
{
for (j=y;j<=yy;++j)
{
del(a[xx][j]);
}
--xx;
}
while (y<q[i].y)
{
for (j=x;j<=xx;++j)
{
del(a[j][y]);
}
++y;
}
while (yy>q[i].yy)
{
for (j=x;j<=xx;++j)
{
del(a[j][yy]);
}
--yy;
}
out[q[i].id]=ans;
}
for (i=0;i<m;++i)
{
cout<<out[i]<<endl;
}
return 0;
}
void add(int x)
{
ans=ans+2*cnt[x]+1;
++cnt[x];
}
void del(int x)
{
ans=ans-2*cnt[x]+1;
--cnt[x];
}