Codeforces 1500D - Tiles for Bathroom(貪心+佇列)
首先先講一發我的 \(n^2q\log n\) 的做法,雖然沒有付諸實現並且我也深知它常數巨大過不去,但是我還是決定講一講(大霧
考慮設 \(f(i,j)\) 表示以 \(i,j\) 為左上角,滿足其中不同顏色個數 \(\le q\) 的子正方形中大小最大的那個的邊長,那麼顯然求出 \(f(i,j)\) 後一遍字尾和即可求出答案。於是問題轉化為如何求 \(f(i,j)\)。注意到一個顯然的結論:\(f(i,j)\ge f(i,j-1)-1\),故考慮用類似雙針的方法求解,我們對於每一行動態地維護一個 \(len\) 表示當前子正方形的邊長,每次將 \(len\)
break
,此時的 \(len\) 值就是我們要求的 \(f(i,j)\),並再將 \(len\) 減一併考慮下一個格子 \(f(i,j+1)\),不難發現 \(len\) 最多變化 \(\mathcal O(n)\) 次,因此我們下面需考慮當 \(len\) 增大或減小時不同顏色個數的變化。注意到每次 \(len\) 加 \(1\) 等價於以下兩個操作:
- 將某行 \(x\) 上的 \(l,l+1,l+2,\cdots,r\) 格子加入當前圖形
- 將某列 \(x\) 上的 \(l,l+1,l+2,\cdots,r\)
將 \(len\) 減一也同理。考慮怎樣處理這個加入某行/某列的格子的操作,注意到 \(q\) 最多隻有 \(10\),因此如果新加入的格子中不同顏色個數 \(>10\) 就肯定不合法了,這部分我們就對行/列分別雙針預處理一遍,即對於每一個格子 \((i,j)\) 找出最大的 \(r\) 滿足 \((i,j),(i,j+1),\cdots,(i,r)\) 中不同顏色個數 \(\le q\),以及最大的 \(r\) 滿足 \((i,j),(i+1,j),\cdots,(r,j)\) 中不同顏色個數 \(\le q\),這樣即可判斷新加入的格子中不同顏色個數是否 \(\le q\)
接下來就是正解了,我們考慮對於每個格子 \((i,j)\) 求出以它為右下角,且不同顏色個數 \(\le q\) 的最大正方形的邊長,考慮怎樣暴力求之——我們考察每個在該點左上方出現的顏色,對於每一種顏色我們求出該顏色的點與 \((i,j)\) 切比雪夫距離的最小值 \(d\),顯然如果子正方形要包含該顏色,那麼該子正方形邊長必須 \(\ge d+1\),因此我們找出第 \(q+1\) 大的 \(d\),顯然子正方形邊長必須 \(<d\),否則子正方形中至少有 \(q+1\) 種顏色,而如果 \(d-1\le\min(i,j)\),一定存在一個以 \((i,j)\) 為右下角的邊長為 \(d-1\) 的正方形,答案就是 \(d-1\),否則答案為 \(\min(i,j)\)。
考慮如何求 \(q+1\) 大的 \(d\),對於每一個點我們建立三個長度 \(q+1\) 的陣列 \(L_{i,j},U_{i,j},LU_{i,j}\) 分別表示 \((i,j)\) 左方、上方、左上方出現過的、距離 \((i,j)\) 前 \(q+1\) 大的顏色,按照它們對應的 \(d\) 值從小到大排序。\(L_{i,j}\) 的求法是容易的,直接從 \(L_{i,j-1}\) 加一個點繼承過來即可,\(U_{i,j}\) 也同理。至於 \(LU_{i,j}\),不難發現在 \(LU_{i,j}\) 出現過的顏色,必然在 \(L_{i,j},U_{i-1,j},LU_{i-1,j-1}\) 中至少一者出現過,因此我們把這三個陣列看作一個佇列,每次取出隊首最小的元素即可在 \(\mathcal O(q)\) 時間內求出這三個陣列。
時間複雜度 \(n^2q\)。
正解題解長度小於歪解是啥操作
const int MAXN=1500;
int n,c,a[MAXN+5][MAXN+5],cnt[MAXN+5],vis[MAXN*MAXN+5];
struct dat{
short int x,y;
dat(short int _x=0,short int _y=0):x(_x),y(_y){}
int col(){return a[x][y];}
};
int getdis(int x1,int y1,int x2,int y2){return max(x1-x2,y1-y2);}
dat L[MAXN+5][MAXN+5][12],U[MAXN+5][MAXN+5][12],LU[MAXN+5][MAXN+5][12];
int sL[MAXN+5][MAXN+5],sU[MAXN+5][MAXN+5],sLU[MAXN+5][MAXN+5];
int main(){
scanf("%d%d",&n,&c);
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d",&a[i][j]);
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){
L[i][j][++sL[i][j]]=dat(i,j);
for(int k=1;k<=sL[i][j-1];k++){
if(L[i][j-1][k].col()==a[i][j]) continue;
if(sL[i][j]<=c) L[i][j][++sL[i][j]]=L[i][j-1][k];
}
U[i][j][++sU[i][j]]=dat(i,j);
for(int k=1;k<=sU[i-1][j];k++){
if(U[i-1][j][k].col()==a[i][j]) continue;
if(sU[i][j]<=c) U[i][j][++sU[i][j]]=U[i-1][j][k];
}
for(int p1=1,p2=1,p3=1;(p1<=sL[i][j]||p2<=sU[i-1][j]||p3<=sLU[i-1][j-1])&&sLU[i][j]<=c;){
int mn1=INF,mn2=INF,mn3=INF;
while(p1<=sL[i][j]&&vis[L[i][j][p1].col()]) ++p1;
while(p2<=sU[i-1][j]&&vis[U[i-1][j][p2].col()]) ++p2;
while(p3<=sLU[i-1][j-1]&&vis[LU[i-1][j-1][p3].col()]) ++p3;
if(p1<=sL[i][j]) mn1=getdis(i,j,L[i][j][p1].x,L[i][j][p1].y);
if(p2<=sU[i-1][j]) mn2=getdis(i,j,U[i-1][j][p2].x,U[i-1][j][p2].y);
if(p3<=sLU[i-1][j-1]) mn3=getdis(i,j,LU[i-1][j-1][p3].x,LU[i-1][j-1][p3].y);
int mn=min(mn1,min(mn2,mn3));
if(mn==INF) break;
if(mn==mn1) LU[i][j][++sLU[i][j]]=L[i][j][p1],vis[L[i][j][p1++].col()]=1;
else if(mn==mn2) LU[i][j][++sLU[i][j]]=U[i-1][j][p2],vis[U[i-1][j][p2++].col()]=1;
else LU[i][j][++sLU[i][j]]=LU[i-1][j-1][p3],vis[LU[i-1][j-1][p3++].col()]=1;
}
for(int k=1;k<=sLU[i][j];k++) vis[LU[i][j][k].col()]=0;
}
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){
int d=(sLU[i][j]==c+1)?getdis(i,j,LU[i][j][c+1].x,LU[i][j][c+1].y):INF;
chkmin(d,min(i,j));
// printf("%d %d %d\n",i,j,d);
cnt[d]++;
}
for(int i=n;i;i--) cnt[i]+=cnt[i+1];
for(int i=1;i<=n;i++) printf("%d\n",cnt[i]);
return 0;
}