1. 程式人生 > >[BZOJ]1047 理想的正方形(HAOI2007)

[BZOJ]1047 理想的正方形(HAOI2007)

ans 單調隊列 nbsp div 自己 str 整數和 大整數 bsp

  真·水題。小C本來是不想貼出來的,但是有一股來自東方的神秘力量催促小C發出來。

Description

  有一個a*b的整數組成的矩陣,現請你從中找出一個n*n的正方形區域,使得該區域所有數中的最大值和最小值的差最小。

Input

  第一行為3個整數,分別表示a,b,n的值第二行至第a+1行每行為b個非負整數,表示矩陣中相應位置上的數。每行相鄰兩數之間用一空格分隔。

Output

  僅一個整數,為a*b矩陣中所有“n*n正方形區域中的最大整數和最小整數的差值”的最小值。

Sample Input

  5 4 2
  1 2 5 6

  0 17 16 0
  16 17 2 1
  2 10 2 1
  1 2 2 2

Sample Output

  1

HINT

  2<=a,b<=1000,n<=a,n<=b,n<=1000

Solution

  如果你是按照BZOJ第一頁AC人數做下來的話,你的思路會被前一題稍微套路一下。

  回歸正題,拿到這題我們正常的思路就是枚舉所有矩陣,計算最大最小值更新答案。

  暴力O(n^4),二維線段樹O(n^2logn)……發現可以降維(先做第一維,再做第二維)……發現詢問區間長度固定……

  單調隊列啊……

  每一行都維護兩個單調隊列(最大最小值),a行同時進行維護。

  維護到所有可能的右端點時,把維護的這a個最大/小值拿出來,在列上做一遍單調隊列,順便更新答案。

  時間復雜度O(n^2)。

  題解寫得比較意識流,但小C認為如果你沒懂不是小C的錯。

#include <cstdio>
#include <algorithm>
#include <cstring>
#define MN 1005
#define INF 0x3FFFFFFF
using namespace std;
struct que { int hd,tl,q1[MN],q2[MN]; void clear() {hd=1; tl=0;} int top() {return q2[hd];} void push(int x,int y,int g) { for (;hd<=tl&&((y>q2[tl])^g);--tl); ++tl; q1[tl]=x; q2[tl]=y; } void pop(int x) {for (;hd<=tl&&q1[hd]<=x;++hd);} }sdu[MN],sdd[MN],su,sd; int a[MN][MN]; int n,m,p,ans; inline int read() { int n=0,f=1; char c=getchar(); while (c<0 || c>9) {if(c==-)f=-1; c=getchar();} while (c>=0 && c<=9) {n=n*10+c-0; c=getchar();} return n*f; } int main() { register int i,j; n=read(); m=read(); p=read(); ans=INF; for (i=1;i<=n;++i) for (j=1;j<=m;++j) a[i][j]=read(); for (i=1;i<=n;++i) sdu[i].clear(),sdd[i].clear(); for (i=1;i<=n;++i) for (j=1;j<p;++j) sdu[i].push(j,a[i][j],0),sdd[i].push(j,a[i][j],1); for (i=p;i<=m;++i) { su.clear(); sd.clear(); for (j=1;j<=n;++j) { sdu[j].push(i,a[j][i],0); sdu[j].pop(i-p); sdd[j].push(i,a[j][i],1); sdd[j].pop(i-p); su.push(j,sdu[j].top(),0); su.pop(j-p); sd.push(j,sdd[j].top(),1); sd.pop(j-p); if (j>=p) ans=min(ans,su.top()-sd.top()); } } printf("%d",ans); }

Last Word

  小C才不會告訴你把這題貼出來的原因是小C覺得自己的代碼好看。

[BZOJ]1047 理想的正方形(HAOI2007)