gym101102D Rectangles (rmq+二分)

題意:ios

給你一個n*m(1e3)的矩陣,讓你找出元素所有相同的子矩陣的個數。spa

思路:code

能夠預處理向左和向上的最大相同長度,而後對於每列用rmq維護一個區間最小值,blog

這個值表示向左延伸的長度,而後對於當前的元素,二分查找距離他最近的值小於他的上一個位置,string

而後當前位置的貢獻就是向左延伸的長度*縱座標之差+1(這塊矩陣徹底相同,直接邊長相乘)再加上上一個位置的貢獻。it

總複雜度就是n^2log(n)的io

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <set>
#include <algorithm>
using namespace std;
typedef long long LL;
typedef pair<int,int>pii;
const int N = 1e3+5;
const int mod = 1e9+7;
int T,n,m,a[N][N],u[N][N],l[N][N],t[N];
int dp[N][10],mm[N];
void initrmq(int x){
  for(int i=1;i<=n;++i)
    dp[i][0] = l[i][x];
  for(int j = 1;j<=mm[n];++j)
    for(int i=1;i+(1<<j)-1<=n;++i)
      dp[i][j] = min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
int rmq(int x,int y){
  int k = mm[y-x+1];
  return min(dp[x][k],dp[y-(1<<k)+1][k]);
}
int main(){
   scanf("%d",&T);
   while(T--){
     mm[0] = -1;
     scanf("%d%d",&n,&m);
     for(int i=1;i<=n;++i)
        mm[i] = ((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
     for(int i=1;i<=n;++i)
       for(int j=1;j<=m;++j)
          scanf("%d",&a[i][j]);
      for(int i=1;i<=n;++i){
         l[i][1] = 1;
         for(int j=2;j<=m;++j)
           if(a[i][j]==a[i][j-1])l[i][j] = l[i][j-1]+1;
           else l[i][j] = 1;
      }
      for(int j=1;j<=m;++j){
         u[1][j] = 1;
         for(int i=2;i<=n;++i){
            if(a[i][j]==a[i-1][j])u[i][j] = u[i-1][j]+1;
            else u[i][j] = 1;
         }
      }
      LL ret = 0;
      for(int j=1;j<=m;++j){
         t[1] = l[1][j];
         initrmq(j);
         for(int i=2;i<=n;++i){
            int x = i ,y = i-u[i][j]+1;
            int tmp = rmq(y,x);
            if(tmp>=l[i][j]){
              t[i] = l[i][j]*u[i][j];
              continue;
            }
            int ans;
            while(x>=y){
               int mid = x+y>>1;
               tmp = rmq(mid,i);
               if(tmp<l[i][j])y = mid+1;
               else ans = mid,x = mid-1;
            }
            t[i] = l[i][j]*(i-ans+1);
            if(i-u[i][j]+1<=ans-1)t[i]+=t[ans-1];
         }
         for(int i=1;i<=n;++i)ret+=t[i];
      }
      printf("%I64d\n",ret);
   }
   return 0;
}
相關文章
相關標籤/搜索