输入一个n*m的矩阵,矩阵的每一个元素都是一个整数,然后有q个询问,每次询问一个子矩阵的权值。矩阵的权值是这样定义的,对于一个整数x,如果它在该矩阵中出现了p次,那么它给该矩阵的权值就贡献 p 2 p^2 p2。
二维莫队的模板题,但是复杂度好像不太明确。。。
于是可以换一种思路,考虑平方的几何意义,即两个颜色相同的点可以构成一个点对,每一次询问在某矩阵中出现的点对个数。
对于每一个矩阵我们拆成(x1,y1,x2,y2),每一个点对同样也是(x1’,y1’,x2’,y2’),即每次询问 x 1 ′ ≥ x 1 , y 1 ′ ≥ y 1 , x 2 ′ ≤ x 2 , y 2 ′ ≤ y 2 x_1'\geq x_1,y_1'\geq y_1,x_2'\leq x_2,y_2'\leq y_2 x1′≥x1,y1′≥y1,x2′≤x2,y2′≤y2的点对个数,可以离线一维之后用三维树状数组即可。
但是上面的做法复杂度会有问题,对于出现次数很多的颜色,它缩能够构成的点对就有很多,但是这种颜色本身必定也很少,于是我们只要对于颜色的出现次数分治一下就好了,对于出现次数较多的颜色直接用二维前缀和来维护。
#include
#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define y1 fuckyou
#define pii pair
#define fi first
#define se second
#define mk make_pair
typedef long long ll;
using namespace std;
void File(){
freopen("bzoj2639.in","r",stdin);
freopen("bzoj2639.out","w",stdout);
}
template<typename T>void read(T &_){
T __=0,mul=1; char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')mul=-1;
ch=getchar();
}
while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
_=__*mul;
}
const int maxn=200+10;
const int maxm=1e5+10;
int n,m,q,a[maxn][maxn],b[maxn*maxn],tot,ans[maxm];
vector<pii>pos[maxn*maxn];
int lim=100,sum[maxn<<1][maxn][maxn],cnt,vis[maxn*maxn];
struct BIT{
int s[maxn<<1][maxn][maxn];
int lowbit(int x){return x&(-x);}
void add(int x,int y,int z){
for(int i=x;i<=m;i+=lowbit(i))
for(int j=y;j<=n;j+=lowbit(j))
for(int k=z;k<=m;k+=lowbit(k))
++s[i][j][k];
}
int query(int x,int y,int z){
int ret=0;
for(int i=x;i>=1;i-=lowbit(i))
for(int j=y;j>=1;j-=lowbit(j))
for(int k=z;k>=1;k-=lowbit(k))
ret+=s[i][j][k];
return ret;
}
}T;
void init(){
read(n); read(m); read(q);
REP(i,1,n)REP(j,1,m)read(a[i][j]),b[++tot]=a[i][j];
sort(b+1,b+tot+1);
tot=unique(b+1,b+tot+1)-b-1;
REP(i,1,n)REP(j,1,m)a[i][j]=lower_bound(b+1,b+tot+1,a[i][j])-b;
REP(i,1,n)REP(j,1,m)pos[a[i][j]].push_back(mk(i,j));
REP(i,1,n)REP(j,1,m)if(!vis[a[i][j]] && (int)pos[a[i][j]].size()>lim){
vis[a[i][j]]=1;
++cnt;
REP(f,1,n)REP(g,1,m)
sum[cnt][f][g]=sum[cnt][f-1][g]+sum[cnt][f][g-1]-sum[cnt][f-1][g-1]+(a[f][g]==a[i][j]);
pos[a[i][j]].clear();
}
}
int cnt_n;
struct node{
int x1,y1,x2,y2,id;
bool operator < (const node & tt) const {
return x1>tt.x1;
}
}ask[maxm],nod[maxn*2*maxn*maxn];
void work(){
REP(i,1,n)REP(j,1,m)if(!vis[a[i][j]]){
int id=a[i][j];
vis[id]=1;
//cout<
REP(f,0,pos[id].size()-1){
REP(g,0,pos[id].size()-1){
int x1=min(pos[id][f].fi,pos[id][g].fi);
int x2=max(pos[id][f].fi,pos[id][g].fi);
int y1=min(pos[id][f].se,pos[id][g].se);
int y2=max(pos[id][f].se,pos[id][g].se);
//cout<
nod[++cnt_n]=(node){x1,y1,x2,y2,0};
}
}
//cout<<"------------"<
}
sort(nod+1,nod+cnt_n+1);
REP(i,1,q)read(ask[i].x1),read(ask[i].y1),read(ask[i].x2),read(ask[i].y2),ask[i].id=i;
sort(ask+1,ask+q+1);
int p=1;
REP(i,1,q){
while(p<=cnt_n && nod[p].x1>=ask[i].x1)T.add(m-nod[p].y1+1,nod[p].x2,nod[p].y2),++p;
int x1=ask[i].x1,y1=ask[i].y1,x2=ask[i].x2,y2=ask[i].y2;
ans[ask[i].id]+=T.query(m-y1+1,x2,y2);
REP(j,1,cnt)ans[ask[i].id]+=
(sum[j][x2][y2]-sum[j][x1-1][y2]-sum[j][x2][y1-1]+sum[j][x1-1][y1-1])
*(sum[j][x2][y2]-sum[j][x1-1][y2]-sum[j][x2][y1-1]+sum[j][x1-1][y1-1]);
//cout<
}
REP(i,1,q)printf("%d\n",ans[i]);
}
int main(){
File();
init();
work();
cerr<<(double)clock()/CLOCKS_PER_SEC<<endl;
return 0;
}