[COCI2010-2011#2] CRNI(單調棧)
阿新 • • 發佈:2020-08-23
[COCI2010-2011#2] CRNI(單調棧)
問題分析
首先考慮兩個不相交的矩形可能存在的位置關係,我將其分成
1.左右
2.上下
3.左上右下
4.左下右上
發現1,2,3,4之間有相交,考慮四種情況的答案應該是1+2-3-4
統計方法
核心: 統計以一個點作為頂點的矩形數量
以統計\(i,j\)為右下角的矩形為例,先不考慮矩形大小>1的限制
顯然可以線上性時間內處理得到每個\(i,j\)向上連續延伸的連續1長度,設其為\(U_{i,j}\)
假設枚舉了\(i\),從左到右依次掃描\(j\),則得到\(i,j\)位置的答案應該是
\[\sum_{k=1}^{j} min_{d=k}^j\lbrace U_{i,d}\rbrace \]
這條式子中,相當於枚舉了\(i,(k,j)\)為底,統計向上延伸的最長長度
這個式子可以用單調棧線上性時間內求解,其過程可以描述為
1.每次插入元素\(U_{i,j}\),得到它的影響區間\(k\in [L,j]\)
2.將原先單調棧內\(k\in [L,j]\)這段區間的答案減掉,改為\(U_{i,j}\cdot (j-L+1)\)
類似的,可以通過改變迴圈順序和額外記錄向下延伸的長度\(D_{i,j}\)來統計四種頂點的答案(詳細見程式碼)
然後可以用字首和幫助統計以上4種答案,列舉一個端點,另一個查詢字首和即可
tips: 注意累和順序,字首和要開long long
#include<bits/stdc++.h> using namespace std; typedef long long ll; #define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i) #define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i) char IO; template <class T=int> T rd(){ T s=0; int f=0; while(!isdigit(IO=getchar())) if(IO=='-') f=1; do s=(s<<1)+(s<<3)+(IO^'0'); while(isdigit(IO=getchar())); return f?-s:s; } const int N=1e3+10; int n; char a[N][N]; int D[N][N],U[N][N]; //i,j向下/上延伸的最長長度 int stk[N],c[N],top; int CRR[N][N]; // 以i,j為右下角的矩形個數 int CLL[N][N]; // 以i,j為左上角的矩形個數 int CLR[N][N]; // 以i,j為右上角的矩形個數 int CRL[N][N]; // 以i,j為左下角的矩形個數 ll SLL[N][N],SRL[N][N]; // 字首和 int main(){ n=rd(); rep(i,1,n) scanf("%s",a[i]+1); rep(i,1,n) rep(j,1,n) if(a[i][j]=='C') U[i][j]=U[i-1][j]+1; drep(i,n,1) rep(j,1,n) if(a[i][j]=='C') D[i][j]=D[i+1][j]+1; rep(i,1,n) { // 統計四種端點的情況 top=0; int now=0; rep(j,1,n) { int x=U[i][j],cnt=1; while(top && stk[top]>=x) cnt+=c[top],now-=c[top]*stk[top],top--; stk[++top]=x,c[top]=cnt; now+=x*cnt; CRR[i][j]=max(now-1,0); } now=top=0; rep(j,1,n) { int x=D[i][j],cnt=1; while(top && stk[top]>=x) cnt+=c[top],now-=c[top]*stk[top],top--; stk[++top]=x,c[top]=cnt; now+=x*cnt; CLR[i][j]=max(now-1,0); } now=top=0; drep(j,n,1) { int x=U[i][j],cnt=1; while(top && stk[top]>=x) cnt+=c[top],now-=c[top]*stk[top],top--; stk[++top]=x,c[top]=cnt; now+=x*cnt; CRL[i][j]=max(now-1,0); } now=top=0; drep(j,n,1) { int x=D[i][j],cnt=1; while(top && stk[top]>=x) cnt+=c[top],now-=c[top]*stk[top],top--; stk[++top]=x,c[top]=cnt; now+=x*cnt; CLL[i][j]=max(now-1,0); } } drep(i,n,1) drep(j,n,1) SLL[i][j]=SLL[i+1][j]+SLL[i][j+1]-SLL[i+1][j+1]+CLL[i][j]; rep(i,1,n) drep(j,n,1) SRL[i][j]=SRL[i-1][j]+SRL[i][j+1]-SRL[i-1][j+1]+CRL[i][j]; // 字首和 ll ans=0; rep(i,1,n) rep(j,1,n) if(CRR[i][j]) ans+=CRR[i][j]*(SLL[i+1][1]+SLL[1][j+1]-SLL[i+1][j+1]); rep(i,1,n) rep(j,1,n) ans-=CLR[i][j]*SRL[i-1][j+1]; // 統計4種情況 printf("%lld\n",ans%10007); }