【ybtoj】【二維Hash】對稱正方形
阿新 • • 發佈:2021-09-06
題意
題解
這題洛谷上有,還是個紫題
根據對稱的定義,再看看範圍,容易想到二分答案(Hash好像經常和二分結合在一起)
二分部分
但是具體怎麼二分?
首先確定,二分成立需要單調性,所以一定是從中心點二分,但是當正方形為奇數的時候中心點在中央的格子,那麼偶數呢?
實際上,邊長為偶數的時候正方形的重心在一個格點(就是一個點,不是格子)
概括一下:
- 對於長度為奇數的正方形,以格子(一個1*1的正方形)為中心二分最遠符合條件的長度
- 對於長度為偶數的正方形,以格點(就是一個點)為中心二分最遠符合條件的長度
那麼二分的 check 函式就可以用二維Hash來O(1)判斷
二維Hash部分
二維Hash的作用就是判斷矩陣是否相同
就是橫向和縱向分別算兩次Hash值(base1,base2取不同的值),但是第二次Hash和第一次Hash略有不同,具體見程式碼
對於Hash值的查詢,類似二維字首和,下面貼的程式碼中 mi1,base1對應縱座標 y ,mi2,base2對應橫座標 x,記住這個對應關係基本就不會寫錯
PS:這道題卡大部分模數1e9+7(0pts),1e9+9(20pts),98244353(0pts),然而用 ull 自然溢位就沒事(100pts)
程式碼
最大子矩陣
#include<bits/stdc++.h> using namespace std; #define ll long long #define ull unsigned long long const int INF = 0x3f3f3f3f,N = 1e3+10,base1 = 233,base2 = 133; const ll mod = 1e9+9; ull mi1[N<<1],mi2[N<<1],sum[N<<1][N<<1]; int len,a[N<<1][N<<1],n,m; int ans; void init() { mi1[0]=mi2[0]=1; //這裡所有的n,m不要忘記×2 for(int i=1;i<=m<<1;i++) mi1[i]=(mi1[i-1]*base1); for(int i=1;i<=n<<1;i++) mi2[i]=(mi2[i-1]*base2); for(int i=1;i<=n<<1;i++) for(int j=1;j<=m<<1;j++) sum[i][j]=sum[i][j-1]*base1+a[i][j]; for(int i=1;i<=n<<1;i++) for(int j=1;j<=m<<1;j++) sum[i][j]+=sum[i-1][j]*base2;//注意這裡是+= } inline ll Hash(int xa,int ya,int xb,int yb) { return sum[xb][yb]-sum[xa-1][yb]*mi2[xb-xa+1]- sum[xb][ya-1]*mi1[yb-ya+1]+ sum[xa-1][ya-1]*mi1[yb-ya+1]*mi2[xb-xa+1]; } inline bool check(int xa,int ya,int xb,int yb) { return Hash(xa,ya,xb,yb)==Hash((n<<1)-xb+1,ya,(n<<1)-xa+1,yb)&& Hash(xa,ya,xb,yb)==Hash(xa,(m<<1)-yb+1,xb,(m<<1)-ya+1);//+1要想好 } int main() { scanf("%d%d",&n,&m); 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++) for(int j=1;j<=m;j++) { a[i+n][j]=a[n-i+1][j]; a[i][j+m]=a[i][m-j+1]; } init();//忘記呼叫init()還調了半天 // for(int i=1;i<=n<<1;i++) // { // for(int j=1;j<=m<<1;j++) // printf("%lld ",sum[i][j]); // puts(""); // } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { //對於長度為奇數的正方形,以格子(一個1*1的正方形)為中心二分最遠符合條件的長度 int l=0,r=max(n,m); while(l<r) { int mid=(l+r+1)>>1; if(i-mid>=1&&j-mid>=1&&i+mid<=n&&j+mid<=m&&check(i-mid,j-mid,i+mid,j+mid)) l=mid; else r=mid-1; } //printf("#1:(%d,%d,%d,%d)\n",i-l,j-l,i+l,j+l); ans+=l+1; //對於長度為偶數的正方形,以格點(就是一個點)為中心二分最遠符合條件的長度 l=0,r=max(n,m); while(l<r) { int mid=(l+r+1)>>1; if(i-mid+1>=1&&j-mid+1>=1&&i+mid<=n&&j+mid<=m&&check(i-mid+1,j-mid+1,i+mid,j+mid)) l=mid; else r=mid-1; } //printf("#2:(%d,%d,%d,%d)\n",i-l+1,j-l+1,i+l,j+l); ans+=l; } printf("%d\n",ans); return 0; }