题目
OvO click here http://acm.hdu.edu.cn/showproblem.php?pid=6052
(2017 Multi-University Training Contest - Team 2 - 1008)
解
分开考虑每种颜色
pre[i][j]代表第j列最近一个i颜色的行数,ppre是次近
calcu(li,ji,x,y,clr)表示从col=li到col=ri,计算(x,y)点对mp[x][y]=clr颜色有贡献的矩形的个数(其实并不是,准确说是(n-i+1)*(calcu(1,m,i,j,mp[i][j])-calcu(1,j-1,i,j,mp[i][j])-calcu(j+1,m,i,j,mp[i][j]))才是有贡献的矩形的个数)
记每个子矩阵中,对于每种颜色,记最上(同层则最左)的那个点对该矩阵该颜色有贡献.
接下来然后搜索每个点,对于每个点用单调栈计算合法矩阵个数,对于点(i,j)就是前文提过的(n-i+1)*(calcu(1,m,i,j,mp[i][j])-calcu(1,j-1,i,j,mp[i][j])-calcu(j+1,m,i,j,mp[i][j]))
calcu函数中,对于每个扫到的列,如果它的高度小于当前栈顶的高度,那么把栈顶的元素弹出来,并且把宽度加到当前这个列中。因为栈中超出当前列的高度的那部分已经不会再对矩形个数产生贡献。
calcu中tmp的作用其实就是能产生贡献的方块的左上角的坐标个数
(本思路来自某其他博客)
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <algorithm> 5 #include <cmath> 6 7 using namespace std; 8 9 typedef long long ll; 10 11 const ll M=124; 12 13 struct node 14 { 15 ll h,x; 16 } stk[M]; 17 18 ll n,m; 19 ll mp[M][M]; 20 ll pre[M*M][M],ppre[M*M][M]; //i,j the row of color=i, in col=j 21 ll lstk; 22 double ans; 23 24 void init() 25 { 26 ans=0; 27 memset(pre,0,sizeof(pre)); 28 memset(ppre,0,sizeof(ppre)); 29 } 30 31 //calcu the num of appropriate matrix whose buttom is row x, from col li to col ri, 32 ll calcu(ll li,ll ri,ll x,ll y,ll clr) 33 { 34 node p; 35 ll k,tmp=0; 36 ll ret=0; 37 lstk=0; 38 for(k=li;k<=ri;k++) 39 { 40 if(pre[clr][k]==x && k>=y) 41 p.h=x-ppre[clr][k]; 42 else 43 p.h=x-pre[clr][k]; 44 p.x=1; 45 if(p.h==0) 46 { 47 tmp=0; 48 lstk=0; 49 continue; 50 } 51 while(lstk>0 && stk[lstk].h>p.h) 52 { 53 tmp-=stk[lstk].h*stk[lstk].x; 54 p.x+=stk[lstk--].x; 55 } 56 stk[++lstk]=p; 57 tmp+=p.h*p.x; 58 ret+=tmp; 59 } 60 return ret; 61 } 62 63 void solve() 64 { 65 ll i,j; 66 for(i=1;i<=n;i++) 67 { 68 for(j=1;j<=m;j++) 69 { 70 ppre[mp[i][j]][j]=pre[mp[i][j]][j]; 71 pre[mp[i][j]][j]=i; 72 } 73 for(j=1;j<=m;j++) 74 ans+=1ll*(n-i+1)*(calcu(1,m,i,j,mp[i][j])-calcu(1,j-1,i,j,mp[i][j])-calcu(j+1,m,i,j,mp[i][j])); 75 } 76 ans=ans*4/(1ll*n*m*(n+1)*(m+1)); 77 printf("%.9lf ",ans); 78 } 79 80 int main() 81 { 82 ll i,j,T; 83 cin>>T; 84 while(T--) 85 { 86 scanf("%lld%lld",&n,&m); 87 for(i=1;i<=n;i++) 88 for(j=1;j<=m;j++) 89 scanf("%lld",&mp[i][j]); 90 init(); 91 solve(); 92 } 93 return 0; 94 }