-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathimos法.html
278 lines (246 loc) · 5.97 KB
/
imos法.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
<title>いもす法</title>
<!--一新、改変OK,ここから-->
原本
<a href="https://imoz.jp/algorithms/imos_method.html">いもす法</a>
<pre>
想定
初期値0の縦R行、横C行の二次元配列Aに
以下のクエリをQ回行う
クエリ:矩形(R1,C1)-(R2,C2)に定数dを足す。
この計算をO(Q + R*C * R*C)で計算する。
元々二次元配列Aに、0以外の値が設定されている場合、
別のアルゴリズムを考えた方が良い。
(Aから勾配配置にO(R*C*R*C)で戻すなど工夫が必要)
--------------------
2次元配列
矩形領域に定数dを足す。
矩形領域は(R1,C1)-(R2,C2)で表すことにします。
R1<=R2 && C1<=C2
ナイーブな実装
<code>
for(int r=R1;r<=R2;r=r+1){
for(int c=C1;c<=C2;c=c+1){
A[r][c] = A[r][c] + d;
}
}
</code>
いもす法は大きく分けて
二つのステップから実行される
1.勾配の配置
2.累積和
例1
縦R行、横C列の2次元配列Aにおいて
矩形(1,1)-(3,3)に1を加算する
<code>
pos1[0] = 1;//row
pos1[1] = 1;//col
pos2[0] = 3;//row
pos2[1] = 3;//col
int add = 1;//加算する値
for(int s=0;s<(1 << 2);++s){
int point[2];//(r,c):勾配を設置する位置
int count = 0;
for(int i=0;i < 2;++i){
if( ( s&(1 << i) ) == 0){
point[i] = pos1[i];
}else{
++count;
point[i] = pos2[i]+1;//要:配列レンジチェック
}
}
if( count%2 == 0 ){//パリティチェック
A[ point[0] ][ point[1] ] += add;
}else{
A[ point[0] ][ point[1] ] -= add;
}
}
</code>
/*
やっていること
A[1][1]に1加算
A[3+1][1]に-1加算
A[1][3+1]に-1加算
A[3+1][3+1]に1加算
*/
初期値
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
勾配の配置
0 0 0 0 0
0 1 0 0 -1
0 0 0 0 0
0 0 0 0 0
0 -1 0 0 1
//c+方向への累積和:A[r][c] += A[r][c-1]
<code>
for(int r=0;r < R;r=r+1){
for(int c=1;c < C;c=c+1){
A[r][c] += A[r][c-1];
}
}
</code>
0 0 0 0 0
0 1 1 1 0
0 0 0 0 0
0 0 0 0 0
0 -1 -1 -1 0
//r+方向への累積和:A[r][c] += A[r-1][c]
<code>
for(int c=0;c < C;c=c+1){
for(int r=1;r < R;r=r+1){
A[r][c] += A[r-1][c];
}
}
</code>
0 0 0 0 0
0 1 1 1 0
0 1 1 1 0
0 1 1 1 0
0 0 0 0 0
矩形(1,1)-(3,3)に1を加算出来た。
-------
例2 複数の矩形をまとめて処理できる。
(1,1)-(3,3)に1加算、(2,2)-(3,4)に2加算
勾配の配置,
0 0 0 0 0
0 1 0 0 -1
0 0 2 0 0 (-2:-2は配置されていない)
0 0 0 0 0
0 -1 -2 0 1 (+2:+2は配置されていない)
c+方向の累積和
0 0 0 0 0
0 1 1 1 0
0 0 2 2 2
0 0 0 0 0
0 -1 -3 -3 -2
r+方向の累積和
0 0 0 0 0
0 1 1 1 0
0 1 3 3 2
0 1 3 3 2
0 0 0 0 0
計算できた
---------------------------
勾配配置を求める
例1でのいもす法の手順は
1.勾配の配置
2.c+方向の累積和
3.r+方向の累積和
で行った。
累積和を逆に行うことで、勾配の配置を求めることができる。
初期値
0 0 0 0 0
0 1 1 1 0
0 1 1 1 0
0 1 1 1 0
0 0 0 0 0
r+方向の差分
<code>
for(int c=0;c < C;c=c+1){//cの順番は不問
for(int r=R-1;r > 0;r=r-1){
A[r][c] -= A[r-1][c];
}
}
</code>
0 0 0 0 0
0 1 1 1 0
0 0 0 0 0
0 0 0 0 0
0 -1 -1 -1 0
c+方向の差分
<code>
for(int r=0;r < R;r=r+1){//rの順番は不問
for(int c=C-1;c > 0;c=c-1){
A[r][c] -= A[r][c-1];
}
}
</code>
0 0 0 0 0
0 1 0 0 -1
0 0 0 0 0
0 0 0 0 0
0 -1 0 0 1
(1,1)-(3,3)の勾配配置が得られた。
未知の勾配配置に対しては
実験的考察が必要だと思います。
うまーく差分を求めていって、
少ない個数の勾配配置を得られれば成功です。
今のところ思いついている累積方向は
A[r][c] += A[r-1][c];
A[r][c] += A[r][c-1];
A[r][c] += A[r-1][c-1];
A[r][c] += A[r+1][c-1];
周囲8近傍/2の4パターンです。
きっと他にもある。
注意1
加算する矩形が(1,1)-(3,3)なのか
(1,1)-(2,2)なのかはA[r][c]の意味に依存します。
注意2
累積方向によって、配置する勾配は変わります。
--------------------------------
どこまでがいもす法なのか、これがわからない。
一次元配列の累積和を用いて
連続区間の和がO(1)で求められた。
同様に、
二次元配列の累積和を用いて
矩形内の和がO(1)で求められる。
*(三次元の立方区間の和もO(1)で求められる。
(x1,y1,z1)-(x2,y2,z2):パリティ計算は同じ。)
*(特殊な座標や特殊な連続区間に対しては、
同様の事が出来るかはわかりません。)
例2で求めた
0 0 0 0 0
0 1 1 1 0
0 1 3 3 2
0 1 3 3 2
0 0 0 0 0
この二次元配列に対して
c+方向の累積とr+方向の累積を実行する。
c+方向の累積
0 0 0 0 0
0 1 2 3 3
0 1 4 7 9
0 1 4 7 9
0 0 0 0 0
r+方向の累積
0 0 0 0 0
0 1 2 3 3
0 2 6 10 12
0 3 10 17 21
0 3 10 17 21
出来た累積和配列を使って矩形(2,2)-(3,3)の総和を求めてみます。
B[3][3] + (-B[2-1][3]) + (-B[3][2-1]) +B[2-1][2-1]
= 17 + (-3) + (-3) + 1 = 12
0 0 0 0
0 1 1 1
0 1 3 3
0 1 3 3
-
0 0 0 0
0 1 1 1
-
0 0
0 1
0 1
0 1
+
0 0
0 1 (1回余分に引いているので足す)
=
_ _ _ _
_ _ _ _
_ _ 3 3
_ _ 3 3
矩形(2,2)-(3,3)の総和が累積和から求められました。
矩形(r1,c1)-(r2,c2)の領域の総和を
累積和配列Bを以下のように用いて求められます。
B[r2][c2]-B[r1-1][c2]-B[r2][c1-1] +B[r1-1][c1-1]
ただし、累積和の方向はc+,r+
</pre>
矩形の総和は、こっちのが詳しい
<a href="http://paiza.hatenablog.com/entry/2014/05/28/%E3%82%82%E3%81%97%E5%A5%B3%E5%AD%90%E5%A4%A7%E7%94%9F%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9E%E3%81%AB%E3%80%8E%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0%E3%80%8F%E3%82%92%E5%9B%B3">ぱいざ</a>
の「O(H^2 W^2 ) の解法」を参照
<!--ここまで-->