数三角形 BZOJ 1201
数三角形
【题目描述】
小苏看到一个这样的等边三角形:该等边三角形每边的长度为n且被分成n等份,于是每条边就有n-1个等分点。而整个三角形被连接两个不同边的等分点且平行于三角形的第三边的线段分成了n2个单位等边三角形(边长为1)。下图左是n=5的情形:
小苏想知道,删除其中的一些短边后,剩下的边一共组成多少个三角形(包括所有边长为m的三角形),正立的和倒立的都算,只要三角形的3m条短边都没有被删除就算是组成一个三角形)。例如,上图右就存在19个三角形。
【输入格式】
大三角形的所有短边可以看成由(n+1)*n/2个单位三角形的边界组成。如下图的灰色三角形所示。其中第1排有1个灰色三角形,第2排有2个灰色三角形,……,第n排有n个灰色三角形。所以输入格式是这样规定的:
从文件中读入数据,文件第一行为正整数n,表示大三角形每边的长度。接下来的n行,第i+1行有i组数,从左到右每组数描述一个三角形,每组数都有3个数,这3个数非0即1,表示对应的短边是否被删除,0表示已被删除,1表示未被删除,依次按照三角形的左、右、下边的顺序来描述。所以第i+1行有3i个数,每个数是0或1。
【输出格式】
输出文件中仅包含一个整数T,表示有多少个三角形的边界都没有被删除。
【输入样例】
5
1 1 1
1 1 0 1 1 0
1 1 1 1 1 1 1 0 1
1 0 1 1 1 1 0 1 1 1 1 1
0 1 1 1 1 1 0 1 1 1 1 1 0 1 1
【输出样例】
19
【数据范围】
1<=n<=1000,1<=m<=n。
题解:
先考虑正三角,枚举底边所在的直线,一个合法三角形的底边是实线,左右两边比底边长
预处理:
c[i].l : 往左上延伸的最长长度
c[i].r : 往右上延伸的最长长度
r[i] : 向右延伸的最长长度
设一条底边的两个编号为 i 和 j
那么一个合法三角形需要满足:
(1)点 i 在点 j 的左边 : i < j -> i >= j
(2)i 到 j 之间为实边: r[i] >= j
(3)左边长于底边 : j - i <= c[i].r -> c[i].r + i >= j
(4)右边长于底边 : j - i <= c[j].l -> i >= j + c[j].l
那么转化为了关于 j 的不等式组
然后将 j 按 j - c[j].l 排序
从大到小枚举 i ,在树状数组中加入满足(4)的点 j
那么答案就是满足 (2) 和 (3) 的元素个数中的较小值减去转化过的条件(1)的元素个数
其实暴力能过(* ̄^ ̄(* ̄^ ̄(* ̄^ ̄)
1 #include<algorithm>
2 #include<iostream>
3 #include<cstring>
4 #include<cstdlib>
5 #include<cstdio>
6 #include<cmath>
7 using namespace std;
8 inline int Get()
9 {
10 int x = 0;
11 char c = getchar();
12 while('0' > c || c > '9') c = getchar();
13 while('0' <= c && c <= '9')
14 {
15 x = (x << 3) + (x << 1) + c - '0';
16 c = getchar();
17 }
18 return x;
19 }
20 struct shape
21 {
22 int l, r, m;
23 };
24 struct mystery
25 {
26 int v, i;
27 };
28 shape a[1233][1233], c[1233][1233];
29 mystery s[1000233];
30 int n;
31 int ans;
32 int r[1233][1233];
33 int tr[1000233];
34 inline void Add(int x, int y)
35 {
36 while(x <= n + 1)
37 {
38 tr[x] += y;
39 x += x & (-x);
40 }
41 }
42 inline int Sum(int x)
43 {
44 int sum = 0;
45 while(x)
46 {
47 sum += tr[x];
48 x -= x & (-x);
49 }
50 return sum;
51 }
52 inline bool rule(mystery a, mystery b)
53 {
54 if(a.v != b.v) return a.v < b.v;
55 return a.i < b.i;
56 }
57 inline void Up()
58 {
59 for(int i = 1; i <= n; ++i)
60 {
61 for(int j = 1; j <= i + 1; ++j)
62 {
63 if(!a[i][j].l) c[i][j].r = 0;
64 else c[i][j].r = c[i - 1][j].r + 1;
65 if(!a[i][j - 1].r) c[i][j].l = 0;
66 else c[i][j].l = c[i - 1][j - 1].l + 1;
67 }
68 for(int j = i + 1; j >= 1; --j)
69 {
70 if(!a[i][j].m) r[i][j] = j;
71 else r[i][j] = r[i][j + 1];
72 }
73 }
74 for(int k = 1; k <= n; ++k)
75 {
76 for(int i = 1; i <= k + 1; ++i)
77 {
78 s[i].v = i - c[k][i].l;
79 s[i].i = i;
80 }
81 sort(s + 1, s + 1 + k + 1, rule);
82 int num = 1;
83 for(int i = 1; i <= k + 1; ++i)
84 {
85 while(num <= k + 1 && s[num].v <= i)
86 {
87 Add(s[num].i, 1);
88 ++num;
89 }
90 if(c[k][i].r && r[k][i])
91 {
92 int one = Sum(c[k][i].r + i);
93 int two = Sum(i);
94 int thr = Sum(r[k][i]);
95 ans += min(one, thr) - two;
96 }
97 }
98 for(int i = 1; i < num; ++i) Add(s[i].i, -1);
99 }
100 }
101 inline void Down()
102 {
103 for(int i = n; i >= 1; --i)
104 {
105 for(int j = 1; j <= i; ++j)
106 {
107 if(!a[i][j].r) c[i][j].r = 0;
108 else c[i][j].r = c[i + 1][j + 1].r + 1;
109 if(!a[i][j].l) c[i][j].l = 0;
110 else c[i][j].l = c[i + 1][j].l + 1;
111 }
112 for(int j = i; j >= 1; --j)
113 {
114 if(!a[i - 1][j].m) r[i][j] = j;
115 else r[i][j] = r[i][j + 1];
116 }
117 }
118 for(int k = 1; k <= n; ++k)
119 {
120 for(int i = 1; i <= k; ++i)
121 {
122 s[i].v = i - c[k][i].l;
123 s[i].i = i;
124 }
125 sort(s + 1, s + 1 + k, rule);
126 int num = 1;
127 for(int i = 1; i <= k; ++i)
128 {
129 while(num <= k && s[num].v <= i)
130 {
131 Add(s[num].i, 1);
132 ++num;
133 }
134
135 if(c[k][i].r && r[k][i])
136 {
137 int one = Sum(c[k][i].r + i);
138 int two = Sum(i);
139 int thr = Sum(r[k][i]);
140 ans += min(one, thr) - two;
141 }
142 }
143 for(int i = 1; i < num; ++i) Add(s[i].i, -1);
144 }
145 }
146 int main()
147 {
148 n = Get();
149 for(int i = 1; i <= n; ++i)
150 for(int j = 1; j <= i; ++j)
151 {
152 a[i][j].l = Get();
153 a[i][j].r = Get();
154 a[i][j].m = Get();
155 }
156 Up();
157 Down();
158 printf("%d", ans);
159 }