BZOJ 1201 [HNOI2005]数三角形:枚举 + 前缀和
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1201
题意:
有一个边长为n的正三角形网格,去掉其中一些线段,问你在这幅图中有多少个三角形。
题解:
枚举 + 前缀和。
三角形总共有两种:正着放的、倒着放的。
分别处理就好。
总复杂度 < O(N^3)
为了判断某一个三角形是否存在,需要迅速判断它的三边是否都是实线(不断开)。
所以建立三个前缀和,分别代表左、右、底边在对应方向上的边长和。
若某一边上的区间和[a,b] == b-a+1,则为实线。
如图为前缀和方向:
正着放的:
N^2枚举三角形最顶上的小三角形,再套一个for枚举向下延伸的边长k。
倒着放的:
倒三角形底部有一个小倒三角形,枚举它左边相邻的小正三角。
AC Code:
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #define MAX_N 1005 5 6 using namespace std; 7 8 int n; 9 int ans=0; 10 int l[MAX_N][MAX_N]; 11 int r[MAX_N][MAX_N]; 12 int b[MAX_N][MAX_N]; 13 int lef[MAX_N][MAX_N]; 14 int rig[MAX_N][MAX_N]; 15 int btm[MAX_N][MAX_N]; 16 17 void read() 18 { 19 memset(l,0,sizeof(l)); 20 memset(r,0,sizeof(r)); 21 memset(b,0,sizeof(b)); 22 cin>>n; 23 for(int i=1;i<=n;i++) 24 { 25 for(int j=1;j<=i;j++) 26 { 27 cin>>l[i][j]>>r[i][j]>>b[i][j]; 28 } 29 } 30 } 31 32 void cal_sum() 33 { 34 memset(lef,0,sizeof(lef)); 35 memset(rig,0,sizeof(rig)); 36 memset(btm,0,sizeof(btm)); 37 for(int i=1;i<=n;i++) 38 { 39 for(int j=1;j<=i;j++) 40 { 41 lef[i][j]=lef[i-1][j]+l[i][j]; 42 rig[i][j]=rig[i-1][j-1]+r[i][j]; 43 btm[i][j]=btm[i][j-1]+b[i][j]; 44 } 45 } 46 } 47 48 void find_tri() 49 { 50 for(int i=1;i<=n;i++) 51 { 52 for(int j=1;j<=i;j++) 53 { 54 for(int k=1;;k++) 55 { 56 if(lef[i+k-1][j]-lef[i-1][j]<k) break; 57 if(rig[i+k-1][j+k-1]-rig[i-1][j-1]<k) break; 58 if(btm[i+k-1][j+k-1]-btm[i+k-1][j-1]<k) continue; 59 ans++; 60 } 61 } 62 } 63 for(int i=1;i<=n;i++) 64 { 65 for(int j=1;j<i;j++) 66 { 67 for(int k=1;i-k>=0 && j-k>=0;k++) 68 { 69 if(lef[i][j+1]-lef[i-k][j+1]<k) break; 70 if(rig[i][j]-rig[i-k][j-k]<k) break; 71 if(btm[i-k][j]-btm[i-k][j-k]<k) continue; 72 ans++; 73 } 74 } 75 } 76 } 77 78 void solve() 79 { 80 cal_sum(); 81 find_tri(); 82 } 83 84 void print() 85 { 86 cout<<ans<<endl; 87 } 88 89 int main() 90 { 91 read(); 92 solve(); 93 print(); 94 }