-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
724 lines (484 loc) · 265 KB
/
atom.xml
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
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>REN Tingxu</title>
<subtitle>former OIer/acmer</subtitle>
<link href="/atom.xml" rel="self"/>
<link href="http://rentingxutx.github.io/"/>
<updated>2019-12-08T09:22:36.154Z</updated>
<id>http://rentingxutx.github.io/</id>
<author>
<name>rtxxx</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>LOJ2541. 「PKUWC2018」猎人杀</title>
<link href="http://rentingxutx.github.io/2019/12/08/LOJ2541-%E3%80%8CPKUWC2018%E3%80%8D%E7%8C%8E%E4%BA%BA%E6%9D%80/"/>
<id>http://rentingxutx.github.io/2019/12/08/LOJ2541-「PKUWC2018」猎人杀/</id>
<published>2019-12-08T08:08:24.000Z</published>
<updated>2019-12-08T09:22:36.154Z</updated>
<content type="html"><![CDATA[<blockquote><p>题目链接:<a href="https://loj.ac/problem/2541" target="_blank" rel="noopener">LOJ2541</a></p></blockquote><h1><span id="solution">Solution</span></h1><p>先尝试dp啊,通分啊搞一搞,发现无论怎么搞都会很多很难受的分母,搞不出来,如果可以把分母的形式简化就好了<br>本题第一个想法,也是最大的思维难点:</p><blockquote><p>发现可以将问题转化为:每一个人可以选多次,问1号最后一个被选的概率,这样与原问题是等价的</p></blockquote><p>感性考虑一下,如果一个人多次被选,大不了这几次都不算,没啥影响。具体证明也不难,设$P$为当前状态下第i个人被选的概率,$S = \sum w_i$,$D= \sum_{i\ is\ already\ dead} w_i$,根据题意显然有<br>$$ P = \frac{w_i}{S - D} $$<br>同时,如果我们用转换后的题意,当前可以选择杀死第i个人,也可以选择杀一个死者,如果鞭尸的话下一步的状态和当前完全相同,也就是<br>$$ P = \frac{w_i}{S} + \frac{D}{S}P $$<br>将这个关于$P$的方程解出来就得到原题的式子了</p><p>这样分母就统一了,优点是可以容斥了,考虑枚举集合T表示在在1号之后被杀死的人,枚举i表示第i+1枪把1号杀死了<br>$$<br>ans = \sum(-1)^{|T|}\sum_{i=0}^{+\infin}(\frac{S - S_T - w_1}{S})^i\frac{w_1}{S} \\<br>= \sum(-1)^{|T|}\frac{1}{1-\frac{S - S_T - w_1}{S}}\frac{w_1}{S}\\ = \sum(-1)^{|T|}\frac{w_1}{S_T + w_1}<br>$$<br>于是可以使用背包dp,f[i][S]表示考虑前i个,$\sum w$恰好为S的带上容斥系数的方案数,有$$<br>f[i][x] = f[i-1][x] - f[i-1][x - w[i]]<br>$$<br>构造生成函数$\Pi(1-x^{w_i})$,可以发现f[S]就是展开式中$x^S$的系数<br>用分治法将该式展开,每次二分找到一个两边次数和最接近的分界点,递归处理两边,然后NTT卷积起来,可以保证复杂度是$O(nlog^2n)$的</p><p>统计答案时枚举$S_T$,统计上面推出来的式子即可<br><a id="more"></a></p><h1><span id="code">Code</span></h1><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> rep(i, a, b) for(int i(a), i##_ORZ(b); i <= i##_ORZ; i++)</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> drep(i, a, b) for(int i(a), i##_ORZ(b); i >= i##_ORZ; i--)</span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> maxn = <span class="number">200010</span>, mod = <span class="number">998244353</span>;</span><br><span class="line"><span class="keyword">int</span> n, w[maxn], s[maxn], rev[maxn * <span class="number">2</span>];</span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">long</span> <span class="keyword">long</span> ll;</span><br><span class="line">ll f[maxn * <span class="number">2</span>], a[maxn * <span class="number">2</span>], b[maxn * <span class="number">2</span>];</span><br><span class="line"><span class="function">ll <span class="title">Power</span><span class="params">(ll x, ll p)</span> </span>{</span><br><span class="line">ll res = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">for</span>(; p; p >>= <span class="number">1</span>, x = x * x % mod) <span class="keyword">if</span>(p & <span class="number">1</span>) res = res * x % mod;</span><br><span class="line"><span class="keyword">return</span> res;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">ntt</span><span class="params">(ll *p, <span class="keyword">int</span> N, <span class="keyword">int</span> inv)</span> </span>{</span><br><span class="line">rep(i, <span class="number">1</span>, N - <span class="number">1</span>) <span class="keyword">if</span>(i < rev[i]) swap(p[i], p[rev[i]]);</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> mid = <span class="number">1</span>; mid < N; mid <<= <span class="number">1</span>) {</span><br><span class="line">ll omega = Power(<span class="number">3</span>, (mod - <span class="number">1</span>) / mid / <span class="number">2</span>);</span><br><span class="line"><span class="keyword">if</span>(inv < <span class="number">0</span>) omega = Power(omega, mod - <span class="number">2</span>);</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i < N; i += (mid << <span class="number">1</span>)) {</span><br><span class="line">ll w = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> j = <span class="number">0</span>; j < mid; j++, w = w * omega % mod) {</span><br><span class="line">ll x = p[i + j], y = w * p[i + j + mid] % mod;</span><br><span class="line">p[i + j] = (x + y) % mod; p[i + j + mid] = (x - y + mod) % mod;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span>(inv < <span class="number">0</span>) {</span><br><span class="line">ll tmp = Power(N, mod - <span class="number">2</span>);</span><br><span class="line">rep(i, <span class="number">0</span>, N - <span class="number">1</span>) p[i] = p[i] * tmp % mod;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">solve</span><span class="params">(<span class="keyword">int</span> l, <span class="keyword">int</span> r, <span class="keyword">int</span> L, <span class="keyword">int</span> R)</span> </span>{</span><br><span class="line"><span class="keyword">if</span>(l == r) {</span><br><span class="line">f[L] = <span class="number">1</span>;</span><br><span class="line">f[L + w[l]] = mod - <span class="number">1</span>;</span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">int</span> ml = l, mr = r - <span class="number">1</span>;</span><br><span class="line"><span class="keyword">while</span>(ml < mr) {</span><br><span class="line"><span class="keyword">int</span> mid = (ml + mr) >> <span class="number">1</span>;</span><br><span class="line"><span class="keyword">if</span>(s[mid] - s[l - <span class="number">1</span>] < s[r] - s[mid]) ml = mid + <span class="number">1</span>; <span class="keyword">else</span> mr = mid;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">int</span> mid = ml, sl = s[mid] - s[l - <span class="number">1</span>] + mid - l + <span class="number">1</span>, sr = s[r] - s[mid] + r - mid;</span><br><span class="line">solve(l, mid, L, L + sl - <span class="number">1</span>); solve(mid + <span class="number">1</span>, r, L + sl, R);</span><br><span class="line"><span class="keyword">int</span> len = sl + sr, N = <span class="number">1</span>, wei = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">while</span>(N <= len) N <<= <span class="number">1</span>, wei++;</span><br><span class="line">rep(i, <span class="number">1</span>, N) rev[i] = (rev[i >> <span class="number">1</span>] >> <span class="number">1</span>) | ((i & <span class="number">1</span>) << (wei - <span class="number">1</span>)), a[i] = b[i] = <span class="number">0</span>;</span><br><span class="line">a[<span class="number">0</span>] = b[<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line">rep(i, L, L + sl - <span class="number">1</span>) a[i - L] = f[i];</span><br><span class="line">rep(i, L + sl, R) b[i - L - sl] = f[i];</span><br><span class="line">ntt(a, N, <span class="number">1</span>); ntt(b, N, <span class="number">1</span>);</span><br><span class="line">rep(i, <span class="number">0</span>, N - <span class="number">1</span>) a[i] = a[i] * b[i] % mod;</span><br><span class="line">ntt(a, N, <span class="number">-1</span>);</span><br><span class="line">rep(i, L, R) f[i] = a[i - L];</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%d"</span>, &n);</span><br><span class="line">rep(i, <span class="number">0</span>, n - <span class="number">1</span>) <span class="built_in">scanf</span>(<span class="string">"%d"</span>, &w[i]);</span><br><span class="line">rep(i, <span class="number">1</span>, n - <span class="number">1</span>) s[i] = s[i<span class="number">-1</span>] + w[i];</span><br><span class="line">solve(<span class="number">1</span>, n - <span class="number">1</span>, <span class="number">0</span>, s[n - <span class="number">1</span>] + n - <span class="number">1</span>);</span><br><span class="line">ll ans = <span class="number">0</span>;</span><br><span class="line">rep(i, <span class="number">0</span>, s[n - <span class="number">1</span>]) {</span><br><span class="line">ans = (ans + f[i] * w[<span class="number">0</span>] % mod * Power((w[<span class="number">0</span>] + i) % mod, mod - <span class="number">2</span>) % mod) % mod;</span><br><span class="line">}</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%lld\n"</span>, ans);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>题目链接:<a href="https://loj.ac/problem/2541" target="_blank" rel="noopener">LOJ2541</a></p>
</blockquote>
<h1 id="Solution"><a href="#Solution" class="headerlink" title="Solution"></a>Solution</h1><p>先尝试dp啊,通分啊搞一搞,发现无论怎么搞都会很多很难受的分母,搞不出来,如果可以把分母的形式简化就好了<br>本题第一个想法,也是最大的思维难点:</p>
<blockquote>
<p>发现可以将问题转化为:每一个人可以选多次,问1号最后一个被选的概率,这样与原问题是等价的</p>
</blockquote>
<p>感性考虑一下,如果一个人多次被选,大不了这几次都不算,没啥影响。具体证明也不难,设$P$为当前状态下第i个人被选的概率,$S = \sum w_i$,$D= \sum_{i\ is\ already\ dead} w_i$,根据题意显然有<br>$$ P = \frac{w_i}{S - D} $$<br>同时,如果我们用转换后的题意,当前可以选择杀死第i个人,也可以选择杀一个死者,如果鞭尸的话下一步的状态和当前完全相同,也就是<br>$$ P = \frac{w_i}{S} + \frac{D}{S}P $$<br>将这个关于$P$的方程解出来就得到原题的式子了</p>
<p>这样分母就统一了,优点是可以容斥了,考虑枚举集合T表示在在1号之后被杀死的人,枚举i表示第i+1枪把1号杀死了<br>$$<br>ans = \sum(-1)^{|T|}\sum_{i=0}^{+\infin}(\frac{S - S_T - w_1}{S})^i\frac{w_1}{S} \\<br>= \sum(-1)^{|T|}\frac{1}{1-\frac{S - S_T - w_1}{S}}\frac{w_1}{S}\\ = \sum(-1)^{|T|}\frac{w_1}{S_T + w_1}<br>$$<br>于是可以使用背包dp,f[i][S]表示考虑前i个,$\sum w$恰好为S的带上容斥系数的方案数,有$$<br>f[i][x] = f[i-1][x] - f[i-1][x - w[i]]<br>$$<br>构造生成函数$\Pi(1-x^{w_i})$,可以发现f[S]就是展开式中$x^S$的系数<br>用分治法将该式展开,每次二分找到一个两边次数和最接近的分界点,递归处理两边,然后NTT卷积起来,可以保证复杂度是$O(nlog^2n)$的</p>
<p>统计答案时枚举$S_T$,统计上面推出来的式子即可<br>
</summary>
<category term="数学" scheme="http://rentingxutx.github.io/categories/%E6%95%B0%E5%AD%A6/"/>
<category term="多项式" scheme="http://rentingxutx.github.io/categories/%E6%95%B0%E5%AD%A6/%E5%A4%9A%E9%A1%B9%E5%BC%8F/"/>
<category term="概率" scheme="http://rentingxutx.github.io/categories/%E6%95%B0%E5%AD%A6/%E6%A6%82%E7%8E%87/"/>
<category term="NTT" scheme="http://rentingxutx.github.io/tags/NTT/"/>
<category term="概率" scheme="http://rentingxutx.github.io/tags/%E6%A6%82%E7%8E%87/"/>
<category term="容斥" scheme="http://rentingxutx.github.io/tags/%E5%AE%B9%E6%96%A5/"/>
</entry>
<entry>
<title>Attentions!</title>
<link href="http://rentingxutx.github.io/2019/12/03/Attentions/"/>
<id>http://rentingxutx.github.io/2019/12/03/Attentions/</id>
<published>2019-12-03T11:47:17.000Z</published>
<updated>2019-12-28T11:10:54.739Z</updated>
<content type="html"><![CDATA[<p>请不要再犯如下错误</p><ol><li>无向图边数要$\times2$</li><li>写完代码后检查内存占用</li><li>认真计算时间复杂度,不要想当然</li><li>注意会不会爆int,否则要开long long</li><li>注意会不会爆long long,要不要用unsigned long long</li><li>int型乘法可能会爆int,最好加上1ll <em> … </em> …,尤其是在要取模的时候</li><li>在模意义下的题目,每一步乘法,加法,减法之后都要取模,每一步</li><li>有时候题意明确结果不会爆long long,但中间过程会,要注意(尤其是dp题)</li><li>tarjan、网络流等等要利用反向边的题目,tot的初始值为1而不是0</li><li>注意边界情形,比如某值等于某值的情况</li><li>树剖建线段树时,节点的信息要先从rev数组里取,同样在线段树上操作时,信息要先过seg数组</li><li>。。。</li></ol><p>同样,要注意考试策略和心态</p><p>从THUWC2020的惨痛教训得知,如果一道题没有十足的把握一定可以写出来正解,那么先去写好暴力,然后挑部分分写,一定不要死磕正解(指的是正式考试,平时练习不算),因为第一正解可能细节考虑不充分,第二可能写不出来,第三可能调不出来。而先写暴力和部分分第一可以启发正解细节部分解决方法,第二写完正解可以对拍更好调,第三有益于稳定心态。<br>有 THUWC本来2.5h写部分分可以写75却非要肝正解结果只有20分暴力分顺利从三等奖挂成二等奖 的具体事例为证。谨记。</p><p>此文章置顶,并永久补充~</p>]]></content>
<summary type="html">
<p>请不要再犯如下错误</p>
<ol>
<li>无向图边数要$\times2$</li>
<li>写完代码后检查内存占用</li>
<li>认真计算时间复杂度,不要想当然</li>
<li>注意会不会爆int,否则要开long long</li>
<li>注意会不会爆long
</summary>
<category term="注意点" scheme="http://rentingxutx.github.io/categories/%E6%B3%A8%E6%84%8F%E7%82%B9/"/>
</entry>
<entry>
<title>CF1229F. Mateusz and Escape Room</title>
<link href="http://rentingxutx.github.io/2019/10/11/CF1229F-Mateusz-and-Escape-Room/"/>
<id>http://rentingxutx.github.io/2019/10/11/CF1229F-Mateusz-and-Escape-Room/</id>
<published>2019-10-11T10:42:50.000Z</published>
<updated>2019-10-11T12:07:28.881Z</updated>
<content type="html"><![CDATA[<blockquote><p>题目链接:<a href="http://codeforces.com/contest/1229/problem/F" target="_blank" rel="noopener">CF1229F</a></p></blockquote><h1><span id="solution">Solution</span></h1><p>设第i个scale向第i-1个scale移动了$x_i$个硬币,为负则代表向左移动,显然可以列出方程组<br>$$<br>l_i - a_i \le x_{i+1} - x_i \le r_i - a_i<br>$$<br>要求$\sum|x_i|$最小<br>首先考虑对于一个固定的$x_1$如何计算这个最小值。考虑动态规划,令$f[i][x]$表示考虑了前i个,第$x_i$的值为j,最少要移动的硬币数,那么显然有如下转移$$<br>f[i][j] = min{f[i-1][j - r_i + a_i…j - l_i + a_i]} + j<br>$$<br>可以将$f[i]$值看成是关于$x_i$的一个函数$f_i(x)$,自变量是$x_i$的取值,因变量是最小移动数,我们可以维护这个函数,考虑如何从$f_i(x)$转移到$f_{i+1}(x)$<br>每个位置新的函数值是之前的某个区间的最小值再加上横坐标,而这个区间是随着自变量一个一个向右平移的,因此可以把操作看成是以下操作的组合</p><ol><li>整体左右移动</li><li>$ f(x) = min{f(x…x+t)} $</li><li>$ f(x) += |x| $</li></ol><p>可以发现如果原函数是一个单峰函数,那么进行这些操作后仍是单峰函数。该函数的初值是$f(x_1) = |x_1|,\quad f(x) = inf\ (x != x_1) $为开口向上的单峰函数。由这一点又可以发现,第2个操作其实相当于整体左右移动后,再将最小值的长度加长<br>因此每次维护可以简化为如下内容</p><ol><li>将上升部分右移$r_i - a_i$</li><li>将下降部分右移$l_i - a_i$</li><li>$ f(x) += |x|$</li></ol><p>如果维护每个每条斜率相同的线段,那么这些线段的斜率是单调增的,且第3个操作只会将y轴左边的线段斜率减一,右边的线段斜率加一。官方题解使用平衡树维护这些线段的斜率和长度,但实际上只需要一个priority_queue,维护所有斜率变化的转折点。由于斜率每次只会变化1,且恒为整数,因此可以维护每一个整数斜率的转折点,如果一个点的斜率变化超过1,那么这个点会插入队列多次,相当于在这个点斜率多次变化1。</p><p>用两个优先队列维护斜率变化的转折点(可重复),对于第1、2个操作,记录左右部分分别移动的距离,可以方便获取实际位置,对于第3个操作,只会涉及到两个队列之间之多一个元素从一个到另一个里面,需要注意穿过y轴的那个线段,他的斜率变化比较特殊,每次都需要新加入转折点。</p><p>当转移完所有$x_i$后,此时的$f(x_1)$就是要求的最小值(因为构成循环)</p><p>这样对于一个固定的$x_1$,可以$O(nlogn)$求出最小值,然而枚举$x_1$仍需要$O(n)$的时间。令$F(x)$表示如果$x_1 = x$,那么如此转移一轮后得到的最小值。那么可以证明</p><blockquote><p>$F(x)$是一个开口向上的单峰函数</p></blockquote><p>具体证明不会,但可以猜到。。。<br>我的想法是,固定$x_1$后可以得到最优的一组$x_i$,将他们画成一条折线,如果控制$x_{i+1}-x_i$不变,改变$x_1$可以将它上下平移。要求minimize$\sum|x_i|$,也就是x轴要在这些点纵坐标的中位数处,随着折线的上下平移,或者看成x轴的上下平移,这个绝对值之和显然是一个单峰函数<br>但这只是不严谨的理解方式。。。</p><p>由这条性质可以三分$x_1$的值,然后跑一遍维护的过程求出最小值更新答案,时间复杂度$ O(nlognlogS) $,其中$S = \sum a_i$<br><a id="more"></a></p><h1><span id="code">Code</span></h1><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> rep(i, a, b) for(int i(a), i##_ORZ(b); i <= i##_ORZ; i++)</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> drep(i, a, b) for(int i(a), i##_ORZ(b); i >= i##_ORZ; i--)</span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">long</span> <span class="keyword">long</span> ll;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> maxn = <span class="number">70010</span>;</span><br><span class="line"><span class="keyword">const</span> ll linf = <span class="number">1l</span>l << <span class="number">60</span>;</span><br><span class="line"><span class="keyword">int</span> n, a[maxn], L[maxn], R[maxn];</span><br><span class="line"><span class="function">ll <span class="title">solve</span><span class="params">(ll x1)</span> </span>{</span><br><span class="line">priority_queue<ll, <span class="built_in">vector</span><ll>, less<ll> > los;</span><br><span class="line">priority_queue<ll, <span class="built_in">vector</span><ll>, greater<ll> > his;</span><br><span class="line">ll ans = <span class="number">0</span>;</span><br><span class="line">ll loshift = <span class="number">0</span>, hishift = <span class="number">0</span>;</span><br><span class="line">rep(i, <span class="number">1</span>, n + <span class="number">10</span>) {</span><br><span class="line">los.push(x1);</span><br><span class="line">his.push(x1);</span><br><span class="line">}</span><br><span class="line">rep(i, <span class="number">1</span>, n) {</span><br><span class="line">hishift += R[i] - a[i];</span><br><span class="line">loshift += L[i] - a[i];</span><br><span class="line"><span class="keyword">if</span>(his.top() + hishift < <span class="number">0</span>) {</span><br><span class="line">ans -= his.top() + hishift;</span><br><span class="line">los.push(his.top() + hishift - loshift); his.pop();</span><br><span class="line">his.push(-hishift);</span><br><span class="line">his.push(-hishift);</span><br><span class="line">} <span class="keyword">else</span> <span class="keyword">if</span>(los.top() + loshift > <span class="number">0</span>) {</span><br><span class="line">ans += los.top() + loshift;</span><br><span class="line">his.push(los.top() + loshift - hishift); los.pop();</span><br><span class="line">los.push(-loshift);</span><br><span class="line">los.push(-loshift);</span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line">his.push(-hishift);</span><br><span class="line">los.push(-loshift);</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; x1 > his.top() + hishift; i++) {</span><br><span class="line">ll tmp = his.top() + hishift; his.pop();</span><br><span class="line">ans += <span class="number">1l</span>l * i * (min(x1, his.top() + hishift) - tmp);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; x1 < los.top() + loshift; i++) {</span><br><span class="line">ll tmp = los.top() + loshift; los.pop();</span><br><span class="line">ans += <span class="number">1l</span>l * i * (tmp - max(x1, los.top() + loshift));</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> ans;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%d"</span>, &n);</span><br><span class="line">ll S = <span class="number">0</span>;</span><br><span class="line">rep(i, <span class="number">1</span>, n) <span class="built_in">scanf</span>(<span class="string">"%d%d%d"</span>, &a[i], &L[i], &R[i]);</span><br><span class="line">rep(i, <span class="number">1</span>, n) S += a[i];</span><br><span class="line">ll l = -S, r = S;</span><br><span class="line"><span class="keyword">while</span>(l < r - <span class="number">5</span>) {</span><br><span class="line">ll mid = (l + r) >> <span class="number">1</span>;</span><br><span class="line"><span class="keyword">if</span>(solve(mid) < solve(mid + <span class="number">1</span>)) r = mid + <span class="number">1</span>; <span class="keyword">else</span> l = mid;</span><br><span class="line">}</span><br><span class="line">ll ans = linf;</span><br><span class="line"><span class="keyword">for</span>(ll i = l; i <= r; i++) ans = min(ans, solve(i));</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%lld\n"</span>, ans);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>题目链接:<a href="http://codeforces.com/contest/1229/problem/F" target="_blank" rel="noopener">CF1229F</a></p>
</blockquote>
<h1 id="Solution"><a href="#Solution" class="headerlink" title="Solution"></a>Solution</h1><p>设第i个scale向第i-1个scale移动了$x_i$个硬币,为负则代表向左移动,显然可以列出方程组<br>$$<br>l_i - a_i \le x_{i+1} - x_i \le r_i - a_i<br>$$<br>要求$\sum|x_i|$最小<br>首先考虑对于一个固定的$x_1$如何计算这个最小值。考虑动态规划,令$f[i][x]$表示考虑了前i个,第$x_i$的值为j,最少要移动的硬币数,那么显然有如下转移$$<br>f[i][j] = min{f[i-1][j - r_i + a_i…j - l_i + a_i]} + j<br>$$<br>可以将$f[i]$值看成是关于$x_i$的一个函数$f_i(x)$,自变量是$x_i$的取值,因变量是最小移动数,我们可以维护这个函数,考虑如何从$f_i(x)$转移到$f_{i+1}(x)$<br>每个位置新的函数值是之前的某个区间的最小值再加上横坐标,而这个区间是随着自变量一个一个向右平移的,因此可以把操作看成是以下操作的组合</p>
<ol>
<li>整体左右移动</li>
<li>$ f(x) = min{f(x…x+t)} $</li>
<li>$ f(x) += |x| $</li>
</ol>
<p>可以发现如果原函数是一个单峰函数,那么进行这些操作后仍是单峰函数。该函数的初值是$f(x_1) = |x_1|,\quad f(x) = inf\ (x != x_1) $为开口向上的单峰函数。由这一点又可以发现,第2个操作其实相当于整体左右移动后,再将最小值的长度加长<br>因此每次维护可以简化为如下内容</p>
<ol>
<li>将上升部分右移$r_i - a_i$</li>
<li>将下降部分右移$l_i - a_i$</li>
<li>$ f(x) += |x|$</li>
</ol>
<p>如果维护每个每条斜率相同的线段,那么这些线段的斜率是单调增的,且第3个操作只会将y轴左边的线段斜率减一,右边的线段斜率加一。官方题解使用平衡树维护这些线段的斜率和长度,但实际上只需要一个priority_queue,维护所有斜率变化的转折点。由于斜率每次只会变化1,且恒为整数,因此可以维护每一个整数斜率的转折点,如果一个点的斜率变化超过1,那么这个点会插入队列多次,相当于在这个点斜率多次变化1。</p>
<p>用两个优先队列维护斜率变化的转折点(可重复),对于第1、2个操作,记录左右部分分别移动的距离,可以方便获取实际位置,对于第3个操作,只会涉及到两个队列之间之多一个元素从一个到另一个里面,需要注意穿过y轴的那个线段,他的斜率变化比较特殊,每次都需要新加入转折点。</p>
<p>当转移完所有$x_i$后,此时的$f(x_1)$就是要求的最小值(因为构成循环)</p>
<p>这样对于一个固定的$x_1$,可以$O(nlogn)$求出最小值,然而枚举$x_1$仍需要$O(n)$的时间。令$F(x)$表示如果$x_1 = x$,那么如此转移一轮后得到的最小值。那么可以证明</p>
<blockquote>
<p>$F(x)$是一个开口向上的单峰函数</p>
</blockquote>
<p>具体证明不会,但可以猜到。。。<br>我的想法是,固定$x_1$后可以得到最优的一组$x_i$,将他们画成一条折线,如果控制$x_{i+1}-x_i$不变,改变$x_1$可以将它上下平移。要求minimize$\sum|x_i|$,也就是x轴要在这些点纵坐标的中位数处,随着折线的上下平移,或者看成x轴的上下平移,这个绝对值之和显然是一个单峰函数<br>但这只是不严谨的理解方式。。。</p>
<p>由这条性质可以三分$x_1$的值,然后跑一遍维护的过程求出最小值更新答案,时间复杂度$ O(nlognlogS) $,其中$S = \sum a_i$<br>
</summary>
<category term="动态规划" scheme="http://rentingxutx.github.io/categories/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"/>
<category term="凸包" scheme="http://rentingxutx.github.io/categories/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/%E5%87%B8%E5%8C%85/"/>
<category term="优先队列" scheme="http://rentingxutx.github.io/tags/%E4%BC%98%E5%85%88%E9%98%9F%E5%88%97/"/>
<category term="凸包维护" scheme="http://rentingxutx.github.io/tags/%E5%87%B8%E5%8C%85%E7%BB%B4%E6%8A%A4/"/>
<category term="三分" scheme="http://rentingxutx.github.io/tags/%E4%B8%89%E5%88%86/"/>
</entry>
<entry>
<title>数论定理整合</title>
<link href="http://rentingxutx.github.io/2019/10/03/%E6%95%B0%E8%AE%BA%E5%AE%9A%E7%90%86%E6%95%B4%E5%90%88/"/>
<id>http://rentingxutx.github.io/2019/10/03/数论定理整合/</id>
<published>2019-10-03T04:14:19.000Z</published>
<updated>2019-10-03T14:11:00.880Z</updated>
<content type="html"><![CDATA[<p><strong>定理2.1(勾股数组定理)</strong><br>每个本原勾股数组$(a, b, c)$(其中a为奇数,b为偶数)都可由如下公式得出:$$<br>a = st,\quad b = \frac{s^2-t^2}{2},\quad c = \frac{s^2+t^2}{2}<br>$$<br>其中$s \gt t \ge 1 $是任意没有公因数的奇数</p><p><strong>定理6.1(线性方程定理)</strong><br>设$a$与$b$是非零整数,$g = gcd(a, b)$,方程<br>$$ ax+by=g $$<br>总是有一个整数解$(x_1, y_1)$,方程每个解可由<br>$$ (x_1 +k \cdot \frac{b}{g},\quad y_1 - k \cdot\frac{a}{g}) $$得到,其中$k$可为任意整数</p><p>顺便介绍扩展欧几里得算法,现在递归到计算方程$ax+by=gcd(a, b)$<br>$$ (a\%b + b\times(a / b))x + by = gcd(a, b) $$$$<br>b \times (y + a / b \times x) + a \% b \times x = gcd(a, a\% b)<br>$$<br>递归计算$bx+a\%by=gcd(b, a\%b)$,得到解$(x_0, y_0)$,则<br>$$ x = y_0,\quad y = x_0 - a / b \times y_0 $$</p><p><strong>定理8.1(线性同余式定理)</strong><br>设$a$,$c$与$m$是整数,$m\ge 1$,且设$g=gcd(a, m)$。<br>(a)如果$g \nmid c$,则同余式$ax\equiv c\ (mod\ m)$没有解<br>(b)如果$g \mid c$,则同余式$ax\equiv c\ (mod\ m) $恰好有$g$个不同的解。先求线性方程<br>$$ au + mv = g$$的一个解$(u_0, v_0)$,则$x_0=cu_0/g$是$ax\equiv c\ (mod\ m)$的解,不同余解的完全集由<br>$$ x\equiv x_0 +k\cdot \frac{m}{k}(mod\ m),\quad k=0, 1, 2, \cdots, g - 1$$给出</p><p><strong>定理8.2(模p多项式根定理)</strong><br>设$p$为素数,$$ f(x)=a_0x^d+a_1x^{d-1}+\cdots +a_d$$<br>是次数为$d\ge 1$的整系数多项式,且$p$不整除$a_0$,则同余式$$ f(x)\equiv 0\ (mod\ p)$$<br>最多有$d$个模$p$的不同余的解。</p><p><strong>定理9.1(费马小定理)</strong><br>设$p$是素数,$a$是任意整数且$a\not\equiv 0\ (mod\ p)$,则<br>$$ a^{p-1}\equiv 1\ (mod\ p) $$</p><p><strong>引理9.2</strong><br>设$p$是素数,$a$是任意整数且$a\not\equiv 0\ (mod\ p)$,则数<br>$$ a, 2a, 3a, \cdots, (p-1)a\quad (mod\ p) $$<br>与数$$ 1, 2, 3,\cdots, (p-1)\quad (mod\ p) $$<br>相同,尽管他们的次序不同</p><p><strong>定理10.1(欧拉公式)</strong><br>如果$gcd(a, m)=1$,则$$ a^{\phi(m)}\equiv 1\ (mod\ m) $$</p><p><strong>引理10.2</strong><br>如果$gcd(a, m)=1$,则数列<br>$$ b_1a, b_2a, b_3a, \cdots, b_{\phi(m)}a\quad (mod\ m) $$<br>与数$$ b_1, b_2, b_3,\cdots, b_{\phi(m)}\quad (mod\ m) $$<br>相同,尽管他们的次序不同</p><p><strong>定理11.1($\phi$函数公式)</strong><br>(a)如果$p$是素数且$k\ge 1$,则$$ \phi(p^k)=p^k-p^{k-1} $$<br>(b)如果$gcd(m, n)=1$,则$ \phi(mn)=\phi(m)\phi(n) $</p><p><strong>定理15.3($\sigma$函数公式)</strong><br>(a)如果$p$是素数且$k\ge 1$,则$$ \sigma(p^k)=\frac{p^{k+1}-1}{p-1} $$<br>(b)如果$gcd(m, n)=1$,则$ \sigma(mn)=\sigma(m)\sigma(n) $</p><p><strong>算法17.1(如何计算模$m$的$k$次根)</strong><br>设$b$, $k$与$m$是已知整数,满足$gcd(b, m)=1 $与$ gcd(k, \phi(m)) = 1 $,下述步骤给出同余式$$<br>x^k\equiv b\ (mod\ m)$$的解</p><ol><li>计算$\phi(m)$</li><li>求满足$ ku-\phi(m)v=1 $的正整数$u$与$v$</li><li>用快速幂计算$b^u\ (mod\ m)$</li></ol><p>正确性证明如下$$ x^k = b^{uk} = b^{1+\phi(m)v}= b\cdot (b^{\phi(m)})^v\equiv b\ (mod\ m) $$<br>由于有两个互质的条件,因此这个解法有一定局限性,后文会讨论$k=2$时的二次剩余问题</p><p><strong>定理19.2(素数的一个性质)</strong><br>设$p$是奇素数,记$p-1=2^kq$,$q$是奇数<br>设$a$是不被$p$整除的整数。则下述两个条件之一成立:<br>(i)$a^q$模$p$余1<br>(ii)数$ a^1, a^{2q}, a^{2^2q}, \cdots, a^{2^{k-1}q} $之一模$p$余-1</p><p><strong>定理19.3(合数的拉宾-米勒测试)</strong><br>设$p$是奇素数,记$p-1=2^kq$,$q$是奇数,对不被n整除的某个a,如果下述两个条件都成立,则$n$是合数<br>(i)$a^q\not\equiv1\ (mod\ n) $<br>(ii)对所有$i=1, 2, \cdots, k-1,\quad a^{2^iq}\not\equiv -1\ (mod\ n) $</p><blockquote><p>如果$n$是奇数,则$1$到$n-1$之间至少有75%的数可作为n的拉宾-米勒证据</p></blockquote><p><strong>定理20.1</strong><br>设$p$为一个奇素数,则恰有$\frac{p-1}{2}$个模p的二次剩余,且恰有$\frac{p-1}{2}$个模p的二次非剩余</p><p><strong>定理20.2(二次剩余乘法法则)</strong><br>$$ QR \times QR = QR,\quad QR\times NR = NR,\quad NR\times NR = QR $$$$<br>\left(\frac{a}{p}\right)\left(\frac{b}{p}\right) = \left( \frac{ab}{p}\right) $$</p><p><strong>定理22.2(广义二次互反律)</strong><br>设$a$,$b$为正奇数,则<br>$$<br>\left(\frac{-1}{b}\right) = \begin{cases} 1 & \text {当$b\equiv 1\ (mod\ 4)$时} \\ -1 & \text {当$b\equiv 3\ (mod\ 4)$时} \end{cases}<br>$$$$<br>\left(\frac{2}{b}\right) = \begin{cases} 1 & \text {当$b\equiv 1或7\ (mod\ 8)$时} \\ -1 & \text {当$b\equiv 3或5\ (mod\ 8)$时} \end{cases}<br>$$$$<br>\left(\frac{a}{b}\right) = \begin{cases} \left(\frac{b}{a}\right) & \text {当$a\equiv 1\ (mod\ 4)$或$b\equiv 1\ (mod\ 4)$时} \\ -\left(\frac{b}{a}\right) & \text {当$a\equiv b\equiv 3\ (mod\ 4)$时} \end{cases}<br>$$<br>需要注意的是,我们只允许对正奇数$a$翻转$\left(\frac{a}{b}\right)$,如果是偶数,则必须先分理出$\left(\frac{2}{b}\right)$的幂,如果是负数,则必须分解出$\left(\frac{-1}{b}\right)$</p><p>此处对于b为任意奇数的情况,该符号为雅可比符号,其定义为:若n可分解为若干不同素数$p_i$的乘积,则<br>$$\left(\frac{a}{n}\right) = \Pi \left(\frac{a}{p_i}\right) $$</p><p><strong>定理24.1(素数的两个平方数之和定理)</strong><br>设p是素数,则p是两个平方数之和的充要条件是$$<br>p\equiv 1\ (mod\ 4)\quad(或p=2) $$</p><p><strong>定理25.1(两平方数之和定理)</strong><br>设m是正整数<br>(a)将m分解为$$m=p_1p_2…p_rM^2$$其中$q_i$是互不相同的素因子,则m可表示成两个平方数之和的虫咬条件是每个$p_i$为2或为模4余1<br>(b)m能表示成两平方数之和$m=a^2+b^2$且$gcd(a, b)=1$,当且仅当一下两个条件之一成立:<br>(i)m是奇数且m的每个素因子都模4余1<br>(ii)m是偶数,m/2是奇数且m/2的每个素因子都模4余1</p><p><strong>阶和原根</strong><br>令$e_p(a)=(使得a^e\equiv 1(mod\ p)的最小指数e\ge 1)$,也可以叫做“阶”</p><p><strong>定理28.1(次数整除性质)</strong><br>设a是不被素数p整除的整数,假设$a^n\equiv 1(mod\ p)$,则次数$e_p(a)$整除n。特别地,次数$e_p(a)$总整除$p-1$</p><p><strong>定理28.2(原根定理)</strong><br>每个素数p都有原根,更精确的,有恰好$\phi(p-1)$个模p的原根<br>进行扩展<br>每个形如$ 2, 4, p^k, 2p^k $(其中p是奇素数)的数n都有原根,更精确的,有恰好$ \phi(\phi(n)) $个原根</p><p>原根的判定可以不用试$1…\phi(n)$的所有数,假设$p_i$是$\phi(n)$的所有质因数,只要验证$$ g^{\frac{\phi(n)}{p_i}} \not\equiv 1\ (mod\ n)$$即可,证明可以用定理28.1</p><p><strong>指标</strong><br>指标具有对数的相似运算法则,需要注意通过模$p-1$来简化指标</p><p><strong>幂函数型高次同余方程</strong><br>通过指标可以求同余式$ ax^k\equiv c\ (mod\ p) $的所有解<br>先讨论p为某素数幂的情况,<br>对同余号两边取指标<br>$$ I(a)+kI(x)\equiv I(c)\ (mod\ p-1) $$<br>利用扩展欧几里得算法解线性同余式$$<br>kI(x)\equiv I(c)-I(a)\ (mod\ p-1)<br>$$最后带入原根快速幂即可解得所有x,求指标可以用BSGS算法<br>如果p不是素数的幂,则可将其质因数分解,设$p=\sum p_i^{c_i}$,则原方程与方程组$ ax^k\equiv c\ (mod\ p_i^{c_i}) $同解,设每个方程的解个数为$T_i$,则原方程有$\Pi T_i$个解,每个解可由原来的解通过中国剩余定理合并得到</p><p><strong>指数函数型高次同余方程</strong><br>使用baby-step-giant-step(BSGS)算法,在$O(\sqrt{n})$的时间复杂度内解决</p><p><strong>定理32.1(佩尔方程定理)</strong><br>设D是一个正整数且不是完全平方数,则佩尔方程<br>$$ x^2-Dy^2=1 $$<br>总有整数解。如果$(x_1, y_1)$是使$x_1$最小的解,则每个解$(x_k, y_k)$可通过取幂得到:<br>$$ x_k + y_k\sqrt{D} = (x_1 + y_1\sqrt{D})^k,\quad k = 1, 2, 3, \cdots $$</p><p><strong>定理33.2(狄利克雷的丢番图逼近定理)</strong><br>假设$\alpha >0$是一个无理数,则存在无穷多个正整数对$(x, y)$使得<br>$$ |x-y\alpha| < \frac{1}{y} $$</p>]]></content>
<summary type="html">
<p><strong>定理2.1(勾股数组定理)</strong><br>每个本原勾股数组$(a, b, c)$(其中a为奇数,b为偶数)都可由如下公式得出:$$<br>a = st,\quad b = \frac{s^2-t^2}{2},\quad c = \frac{s^2+
</summary>
<category term="数学" scheme="http://rentingxutx.github.io/categories/%E6%95%B0%E5%AD%A6/"/>
<category term="数论" scheme="http://rentingxutx.github.io/categories/%E6%95%B0%E5%AD%A6/%E6%95%B0%E8%AE%BA/"/>
<category term="数论" scheme="http://rentingxutx.github.io/tags/%E6%95%B0%E8%AE%BA/"/>
<category term="定理" scheme="http://rentingxutx.github.io/tags/%E5%AE%9A%E7%90%86/"/>
</entry>
<entry>
<title>CF1205E. Expected Value Again</title>
<link href="http://rentingxutx.github.io/2019/09/27/CF1205E-Expected-Value-Again/"/>
<id>http://rentingxutx.github.io/2019/09/27/CF1205E-Expected-Value-Again/</id>
<published>2019-09-27T12:05:51.000Z</published>
<updated>2019-10-11T12:32:33.467Z</updated>
<content type="html"><![CDATA[<blockquote><p>题目链接:<a href="https://codeforces.com/contest/1205/problem/E" target="_blank" rel="noopener">CF1205E</a></p></blockquote><h1><span id="solution">Solution</span></h1><p>令$f(s, i)$表示字符串s中是否有一个长度为j的前后缀,则答案为<br>$$<br>E((\sum_{i=1}^{n-1}f(s, i))^2) \\<br>= \sum_{i=1}^{n-1}\sum_{j=1}^{n-1}E(f(s, i) * f(s, j)))<br>$$<br>表示的意义就是对于任意i和j,求出一个字符串同时有i和j长度的前后缀的期望,也就是有多少个字符串同时有长度为i和j的前后缀,再除以$k^n$<br>重点就是这样一个性质</p><blockquote><p>一个字符串同时有长度为$n-i$和$n-j$的前后缀的期望是<br>$$(\frac{1}{k})^{n-max(gcd(i, j),\ i + j - n)}$$</p></blockquote><p>下面来证明这个性质</p><p>首先,如果有长度为$n-i$的前后缀,那么i一定是一个循环节。这一点容易证明。现在i和j都是循环节,所以$gcd(i, j)$也是循环节,一个循环节内的字符可以任意取,剩下的都是固定的,除以$k^n$得到max的前一部分</p><p>然后,我们有一种感觉,如果$i+j$足够长,那么两个循环节从第二次出现开始不一定会有许多重合的位置,或许gcd相对苛刻了一些。换句话说,如果该字符串无限长,那么一定是gcd为循环节,但长度是有限的,有特殊情况需要考虑。</p><p>举几个例子,用相同的字母表示必须相同的位置,X表示任意<br>一个长度为10的字符串,i=7,j=8<br>AAAXXXXAAA<br>一个长度为11的字符串,i=10,j=3<br>AAXAAXAAXAA<br>显然他们的循环节都不是gcd</p><p>如果$i+j\leq n$,那么答案显然就是gcd,画一个图,较长串第二次出现可以看成一个较短串,这个完整的较短串和全部由较短串拼出的串的对应部分相等,差的部分是循环节,可以递归得到gcd(两数gcd等于差的gcd)</p><p>否则,$i+j\gt n$,不同之处在于较长串第二处出现无法看成一个完整的较短串。不妨令$ i > j $,第二次循环时可以看成一个j,这个j和 完全由j拼成的串 的对应位置产生错位,在j内部产生循环,递归到gcd,如果i后面的那个j超过n的部分段于gcd,那么循环节可以作用在整个j里,成为真正的循环节,否则它是假的循环节,因为无法扩展完整个j,剩下的无法扩展的部分就是$i + j - n - gcd(i, j)$ 这部分作为j这个循环节的一部分可以随便取,再算上gcd可以随便选,结果就是$max(gcd(i, j),\ i + j - n)$</p><p>下面考虑怎么算,枚举$d = gcd(i, j), s = i + j$,转化为求多少对i和j满足定义,注意$ 1 \leq i, j < n $<br>$$<br>f(d) = \sum_{i=1}^{min(n - 1, s - 1)}[gcd(i, s - i) = d] \\<br>F(d) = \sum_{d|t} f(t) = min(s - 1, n - 1) / t;<br>$$<br>$$<br>f(d) = \sum_{d|t}\mu(\frac{t}{d})F(t)<br>$$<br>代码中,由于$ d | t | s $,所以先枚举d,再枚举的是t / d,最后枚举s,复杂度$O(nlog^2n)$,注意i,j的范围<br><a id="more"></a></p><h1><span id="code">Code</span></h1><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> rep(i, a, b) for(int i(a), i##_ORZ(b); i <= i##_ORZ; i++)</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> drep(i, a, b) for(int i(a), i##_ORZ(b); i >= i##_ORZ; i--)</span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> maxn = <span class="number">400010</span>, mod = <span class="number">1e9</span>+<span class="number">7</span>;</span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">long</span> <span class="keyword">long</span> ll;</span><br><span class="line">ll n, k, ans, miu[maxn], pri[maxn], cnt, v[maxn], pk[maxn];</span><br><span class="line"><span class="function">ll <span class="title">Power</span><span class="params">(ll x, ll p)</span> </span>{</span><br><span class="line">ll res = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">for</span>(; p; p >>= <span class="number">1</span>, x = x * x % mod) <span class="keyword">if</span>(p & <span class="number">1</span>) res = res * x % mod;</span><br><span class="line"><span class="keyword">return</span> res;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%lld%lld"</span>, &n, &k);</span><br><span class="line">k = Power(k, mod - <span class="number">2</span>);</span><br><span class="line">miu[<span class="number">1</span>] = <span class="number">1</span>; v[<span class="number">1</span>] = <span class="number">1</span>;</span><br><span class="line">rep(i, <span class="number">2</span>, n) {</span><br><span class="line"><span class="keyword">if</span>(!v[i]) pri[++cnt] = i, miu[i] = <span class="number">-1</span>;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> j = <span class="number">1</span>; j <= cnt && i * pri[j] <= n; j++) {</span><br><span class="line">v[i * pri[j]] = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">if</span>(i % pri[j]) miu[i * pri[j]] = -miu[i];</span><br><span class="line"><span class="keyword">else</span> miu[i * pri[j]] = <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">rep(i, <span class="number">0</span>, n * <span class="number">2</span>) pk[i] = Power(k, i);</span><br><span class="line">rep(d, <span class="number">1</span>, n - <span class="number">1</span>) </span><br><span class="line">rep(t, <span class="number">1</span>, (n * <span class="number">2</span> - <span class="number">2</span>) / d)</span><br><span class="line"><span class="keyword">for</span>(ll s = t * d; s <= n * <span class="number">2</span> - <span class="number">2</span>; s += t * d) {</span><br><span class="line"><span class="keyword">if</span>(s == d) <span class="keyword">continue</span>;</span><br><span class="line">ll tmp = miu[t] * (min(n - <span class="number">1</span>, s - <span class="number">1</span>) / d / t - max(<span class="number">0l</span>l, s - n) / d / t);</span><br><span class="line"><span class="comment">//cout << d << ' ' << t << ' ' << s << ' ' << tmp << endl;</span></span><br><span class="line">ans = (ans + tmp * pk[k, n - max(<span class="number">1l</span>l * d, s - n)] % mod + mod) % mod;</span><br><span class="line">}</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%lld\n"</span>, ans);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>题目链接:<a href="https://codeforces.com/contest/1205/problem/E" target="_blank" rel="noopener">CF1205E</a></p>
</blockquote>
<h1 id="Solution"><a href="#Solution" class="headerlink" title="Solution"></a>Solution</h1><p>令$f(s, i)$表示字符串s中是否有一个长度为j的前后缀,则答案为<br>$$<br>E((\sum_{i=1}^{n-1}f(s, i))^2) \\<br>= \sum_{i=1}^{n-1}\sum_{j=1}^{n-1}E(f(s, i) * f(s, j)))<br>$$<br>表示的意义就是对于任意i和j,求出一个字符串同时有i和j长度的前后缀的期望,也就是有多少个字符串同时有长度为i和j的前后缀,再除以$k^n$<br>重点就是这样一个性质</p>
<blockquote>
<p>一个字符串同时有长度为$n-i$和$n-j$的前后缀的期望是<br>$$(\frac{1}{k})^{n-max(gcd(i, j),\ i + j - n)}$$</p>
</blockquote>
<p>下面来证明这个性质</p>
<p>首先,如果有长度为$n-i$的前后缀,那么i一定是一个循环节。这一点容易证明。现在i和j都是循环节,所以$gcd(i, j)$也是循环节,一个循环节内的字符可以任意取,剩下的都是固定的,除以$k^n$得到max的前一部分</p>
<p>然后,我们有一种感觉,如果$i+j$足够长,那么两个循环节从第二次出现开始不一定会有许多重合的位置,或许gcd相对苛刻了一些。换句话说,如果该字符串无限长,那么一定是gcd为循环节,但长度是有限的,有特殊情况需要考虑。</p>
<p>举几个例子,用相同的字母表示必须相同的位置,X表示任意<br>一个长度为10的字符串,i=7,j=8<br>AAAXXXXAAA<br>一个长度为11的字符串,i=10,j=3<br>AAXAAXAAXAA<br>显然他们的循环节都不是gcd</p>
<p>如果$i+j\leq n$,那么答案显然就是gcd,画一个图,较长串第二次出现可以看成一个较短串,这个完整的较短串和全部由较短串拼出的串的对应部分相等,差的部分是循环节,可以递归得到gcd(两数gcd等于差的gcd)</p>
<p>否则,$i+j\gt n$,不同之处在于较长串第二处出现无法看成一个完整的较短串。不妨令$ i &gt; j $,第二次循环时可以看成一个j,这个j和 完全由j拼成的串 的对应位置产生错位,在j内部产生循环,递归到gcd,如果i后面的那个j超过n的部分段于gcd,那么循环节可以作用在整个j里,成为真正的循环节,否则它是假的循环节,因为无法扩展完整个j,剩下的无法扩展的部分就是$i + j - n - gcd(i, j)$ 这部分作为j这个循环节的一部分可以随便取,再算上gcd可以随便选,结果就是$max(gcd(i, j),\ i + j - n)$</p>
<p>下面考虑怎么算,枚举$d = gcd(i, j), s = i + j$,转化为求多少对i和j满足定义,注意$ 1 \leq i, j &lt; n $<br>$$<br>f(d) = \sum_{i=1}^{min(n - 1, s - 1)}[gcd(i, s - i) = d] \\<br>F(d) = \sum_{d|t} f(t) = min(s - 1, n - 1) / t;<br>$$<br>$$<br>f(d) = \sum_{d|t}\mu(\frac{t}{d})F(t)<br>$$<br>代码中,由于$ d | t | s $,所以先枚举d,再枚举的是t / d,最后枚举s,复杂度$O(nlog^2n)$,注意i,j的范围<br>
</summary>
<category term="其他" scheme="http://rentingxutx.github.io/categories/%E5%85%B6%E4%BB%96/"/>
<category term="性质" scheme="http://rentingxutx.github.io/categories/%E5%85%B6%E4%BB%96/%E6%80%A7%E8%B4%A8/"/>
<category term="莫比乌斯反演" scheme="http://rentingxutx.github.io/tags/%E8%8E%AB%E6%AF%94%E4%B9%8C%E6%96%AF%E5%8F%8D%E6%BC%94/"/>
<category term="性质" scheme="http://rentingxutx.github.io/tags/%E6%80%A7%E8%B4%A8/"/>
<category term="循环节" scheme="http://rentingxutx.github.io/tags/%E5%BE%AA%E7%8E%AF%E8%8A%82/"/>
</entry>
<entry>
<title>BZOJ1566 [NOI2009]管道取珠</title>
<link href="http://rentingxutx.github.io/2019/09/18/BZOJ1566-NOI2009-%E7%AE%A1%E9%81%93%E5%8F%96%E7%8F%A0/"/>
<id>http://rentingxutx.github.io/2019/09/18/BZOJ1566-NOI2009-管道取珠/</id>
<published>2019-09-18T11:51:20.000Z</published>
<updated>2019-09-18T12:10:06.539Z</updated>
<content type="html"><![CDATA[<blockquote><p>题目链接:<a href="https://www.lydsy.com/JudgeOnline/problem.php?id=1566" target="_blank" rel="noopener">BZOJ1566</a></p></blockquote><h1><span id="solution">Solution</span></h1><p>考虑平方转换,$\sum a_i^2$即为两个操作方式得到相同序列的方案数<br>此处使用dp,在状态中同时记录两个操作方式的信息,即可非常方便地决策了<br>考虑如果两个操作方式本来的到相同的序列,然后都加入一个相同的元素,那么之后也是相同的序列,将方案数归入<br>$f[i][u1][u2]$表示已经已经考虑了i个球(上下都算),其中第一个操作方式在上面已经选了u1个,第二个操作方式在上面已经选了u2个,这样可以最节省地刻画状态,通过这三个信息可以计算出两种操作方式分别在下面选了几个<br>转移时有四种可能,两种操作方式都可以从上面或从下面取出一个球,如果两种操作方式取出的球颜色相同则可以转移,累加进新的状态即可<br>具体可以看代码,很清晰也很简洁</p><p>似乎看起来很简单的样子,但真的很难想啊<br>关键是平方转换,并想到在状态中同时维护两个操作方式<br>感觉本来很难统计的东西这样设计状态后就非常容易了,神啊<br><a id="more"></a></p><h1><span id="code">Code</span></h1><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> maxn = <span class="number">510</span>, mod = <span class="number">1024523</span>;</span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">long</span> <span class="keyword">long</span> ll;</span><br><span class="line"><span class="keyword">int</span> n, m, f[<span class="number">2</span>][maxn][maxn];</span><br><span class="line"><span class="keyword">char</span> a[maxn], b[maxn]; </span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">add</span><span class="params">(<span class="keyword">int</span> &x, <span class="keyword">int</span> y)</span> </span>{</span><br><span class="line">x = (x + y) % mod;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"><span class="comment">// freopen("ball.in", "r", stdin);</span></span><br><span class="line"><span class="comment">// freopen("ball.out", "w", stdout);</span></span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%d%d"</span>, &n, &m);</span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%s"</span>, a + <span class="number">1</span>); </span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%s"</span>, b + <span class="number">1</span>);</span><br><span class="line">reverse(a + <span class="number">1</span>, a + <span class="number">1</span> + n); reverse(b + <span class="number">1</span>, b + <span class="number">1</span> + m);</span><br><span class="line">f[<span class="number">0</span>][<span class="number">0</span>][<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> s = <span class="number">0</span>, cur = <span class="number">0</span>; s <= n + m; s++, cur ^= <span class="number">1</span>) {</span><br><span class="line"><span class="built_in">memset</span>(f[cur ^ <span class="number">1</span>], <span class="number">0</span>, <span class="keyword">sizeof</span>(f[cur ^ <span class="number">1</span>]));</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> u1 = <span class="number">0</span>; u1 <= n; u1++) </span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> u2 = <span class="number">0</span>; u2 <= n; u2++) {</span><br><span class="line"><span class="keyword">int</span> d2 = s - u2, d1 = s - u1;</span><br><span class="line"><span class="keyword">int</span> t = f[cur][u1][u2];</span><br><span class="line"><span class="keyword">if</span>(!t || d2 < <span class="number">0</span> || d2 > m || d1 < <span class="number">0</span> || d1 > m) <span class="keyword">continue</span>;</span><br><span class="line"><span class="comment">// cout << u1 << ' ' << d1 << ' ' << u2 << ' ' << d2 << ' ' << t << endl;</span></span><br><span class="line"><span class="keyword">if</span>(a[u1 + <span class="number">1</span>] == a[u2 + <span class="number">1</span>]) add(f[cur ^ <span class="number">1</span>][u1+<span class="number">1</span>][u2+<span class="number">1</span>], t);</span><br><span class="line"><span class="keyword">if</span>(a[u1 + <span class="number">1</span>] == b[d2 + <span class="number">1</span>]) add(f[cur ^ <span class="number">1</span>][u1+<span class="number">1</span>][u2], t);</span><br><span class="line"><span class="keyword">if</span>(b[d1 + <span class="number">1</span>] == a[u2 + <span class="number">1</span>]) add(f[cur ^ <span class="number">1</span>][u1][u2+<span class="number">1</span>], t);</span><br><span class="line"><span class="keyword">if</span>(b[d1 + <span class="number">1</span>] == b[d2 + <span class="number">1</span>]) add(f[cur ^ <span class="number">1</span>][u1][u2], t);</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%d\n"</span>, f[(n + m) & <span class="number">1</span>][n][n]);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>题目链接:<a href="https://www.lydsy.com/JudgeOnline/problem.php?id=1566" target="_blank" rel="noopener">BZOJ1566</a></p>
</blockquote>
<h1 id="Solution"><a href="#Solution" class="headerlink" title="Solution"></a>Solution</h1><p>考虑平方转换,$\sum a_i^2$即为两个操作方式得到相同序列的方案数<br>此处使用dp,在状态中同时记录两个操作方式的信息,即可非常方便地决策了<br>考虑如果两个操作方式本来的到相同的序列,然后都加入一个相同的元素,那么之后也是相同的序列,将方案数归入<br>$f[i][u1][u2]$表示已经已经考虑了i个球(上下都算),其中第一个操作方式在上面已经选了u1个,第二个操作方式在上面已经选了u2个,这样可以最节省地刻画状态,通过这三个信息可以计算出两种操作方式分别在下面选了几个<br>转移时有四种可能,两种操作方式都可以从上面或从下面取出一个球,如果两种操作方式取出的球颜色相同则可以转移,累加进新的状态即可<br>具体可以看代码,很清晰也很简洁</p>
<p>似乎看起来很简单的样子,但真的很难想啊<br>关键是平方转换,并想到在状态中同时维护两个操作方式<br>感觉本来很难统计的东西这样设计状态后就非常容易了,神啊<br>
</summary>
<category term="动态规划" scheme="http://rentingxutx.github.io/categories/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"/>
<category term="计数dp" scheme="http://rentingxutx.github.io/categories/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/%E8%AE%A1%E6%95%B0dp/"/>
<category term="计数dp" scheme="http://rentingxutx.github.io/tags/%E8%AE%A1%E6%95%B0dp/"/>
</entry>
<entry>
<title>BZOJ1564 [NOI2009]二叉查找树</title>
<link href="http://rentingxutx.github.io/2019/09/18/BZOJ1564-NOI2009-%E4%BA%8C%E5%8F%89%E6%9F%A5%E6%89%BE%E6%A0%91/"/>
<id>http://rentingxutx.github.io/2019/09/18/BZOJ1564-NOI2009-二叉查找树/</id>
<published>2019-09-18T10:46:51.000Z</published>
<updated>2019-09-18T11:29:49.397Z</updated>
<content type="html"><![CDATA[<blockquote><p>题目链接:<a href="https://www.lydsy.com/JudgeOnline/problem.php?id=1564" target="_blank" rel="noopener">BZOJ1564</a></p></blockquote><h1><span id="solution">Solution</span></h1><p>最开始看成权值只能改成整数,然后想复杂了很多。。。<br>记得好多名为二叉树的题都是区间dp,本题也是<br>改变权值,按照数据值排序的序列不变,在这个序列上区间dp<br>令$f[i][j][w]$表示$[i, j]$这一个区间构成一棵树,根节点的权值的排名不小于$w$的最小代价<br>决策时枚举这个区间的根是谁,再枚举根的新的权值的排名是多少,如果新的权值小于等于现在的权值,那么不需要+K,代价用前缀和算一下<br>每次分配一个新的权值,枚举的是它要放在当前哪一个权值之前,因此决策时可以不用+1。或者换句话说,权值什么的都不重要,我们只要排出来一个深度的先后顺序就可以了,这样枚举是可以保证一定会考虑到所有先后顺序的</p><a id="more"></a><h1><span id="code">Code</span></h1><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> maxn = <span class="number">80</span>;</span><br><span class="line"><span class="keyword">int</span> f[maxn][maxn][maxn], n, K, ta[maxn], S[maxn];</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Node</span>{</span> <span class="keyword">int</span> v, w, p; }a[maxn];</span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">cmp</span><span class="params">(Node x, Node y)</span> </span>{ <span class="keyword">return</span> x.v < y.v; }</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"><span class="comment">// freopen("treapmod.in", "r", stdin);</span></span><br><span class="line"><span class="comment">// freopen("treapmod.out", "w", stdout);</span></span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%d%d"</span>, &n, &K);</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i++) <span class="built_in">scanf</span>(<span class="string">"%d"</span>, &a[i].v);</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i++) <span class="built_in">scanf</span>(<span class="string">"%d"</span>, &a[i].w), ta[i] = a[i].w;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i++) <span class="built_in">scanf</span>(<span class="string">"%d"</span>, &a[i].p);</span><br><span class="line">sort(a + <span class="number">1</span>, a + <span class="number">1</span> + n, cmp); sort(ta + <span class="number">1</span>, ta + <span class="number">1</span> + n);</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i++) a[i].w = lower_bound(ta + <span class="number">1</span>, ta + <span class="number">1</span> + n, a[i].w) - ta;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i++) S[i] = S[i<span class="number">-1</span>] + a[i].p;</span><br><span class="line"><span class="built_in">memset</span>(f, <span class="number">0x3f</span>, <span class="keyword">sizeof</span>(f));</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= n + <span class="number">1</span>; i++) <span class="keyword">for</span>(<span class="keyword">int</span> w = <span class="number">1</span>; w <= n; w++) f[i][i<span class="number">-1</span>][w] = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> len = <span class="number">1</span>; len <= n; len++) <span class="keyword">for</span>(<span class="keyword">int</span> l = <span class="number">1</span>; l + len - <span class="number">1</span> <= n; l++) {</span><br><span class="line"><span class="keyword">int</span> r = l + len - <span class="number">1</span>;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> w = n; w; w--) <span class="keyword">for</span>(<span class="keyword">int</span> i = l; i <= r; i++) {</span><br><span class="line">f[l][r][w] = min(f[l][r][w], f[l][i<span class="number">-1</span>][w] + f[i+<span class="number">1</span>][r][w] + K + S[r] - S[l<span class="number">-1</span>]);</span><br><span class="line"><span class="keyword">if</span>(w <= a[i].w) f[l][r][w] = min(f[l][r][w], f[l][i<span class="number">-1</span>][a[i].w] + f[i+<span class="number">1</span>][r][a[i].w] + S[r] - S[l<span class="number">-1</span>]);</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="keyword">int</span> ans = <span class="number">0x3f3f3f3f</span>;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i++) ans = min(ans, f[<span class="number">1</span>][n][i]);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%d\n"</span>, ans);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>题目链接:<a href="https://www.lydsy.com/JudgeOnline/problem.php?id=1564" target="_blank" rel="noopener">BZOJ1564</a></p>
</blockquote>
<h1 id="Solution"><a href="#Solution" class="headerlink" title="Solution"></a>Solution</h1><p>最开始看成权值只能改成整数,然后想复杂了很多。。。<br>记得好多名为二叉树的题都是区间dp,本题也是<br>改变权值,按照数据值排序的序列不变,在这个序列上区间dp<br>令$f[i][j][w]$表示$[i, j]$这一个区间构成一棵树,根节点的权值的排名不小于$w$的最小代价<br>决策时枚举这个区间的根是谁,再枚举根的新的权值的排名是多少,如果新的权值小于等于现在的权值,那么不需要+K,代价用前缀和算一下<br>每次分配一个新的权值,枚举的是它要放在当前哪一个权值之前,因此决策时可以不用+1。或者换句话说,权值什么的都不重要,我们只要排出来一个深度的先后顺序就可以了,这样枚举是可以保证一定会考虑到所有先后顺序的</p>
</summary>
<category term="动态规划" scheme="http://rentingxutx.github.io/categories/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"/>
<category term="区间dp" scheme="http://rentingxutx.github.io/categories/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/%E5%8C%BA%E9%97%B4dp/"/>
<category term="区间dp" scheme="http://rentingxutx.github.io/tags/%E5%8C%BA%E9%97%B4dp/"/>
</entry>
<entry>
<title>CF585 (Div. 2)</title>
<link href="http://rentingxutx.github.io/2019/09/16/CF585-Div-2/"/>
<id>http://rentingxutx.github.io/2019/09/16/CF585-Div-2/</id>
<published>2019-09-16T12:33:01.000Z</published>
<updated>2019-09-17T10:52:42.300Z</updated>
<content type="html"><![CDATA[<blockquote><p>题目链接:<a href="https://codeforces.com/contest/1215" target="_blank" rel="noopener">CF585</a></p></blockquote><h1><span id="solution">Solution</span></h1><h2><span id="d-ticket-game">D. Ticket Game</span></h2><p>无法直接SG函数的博弈论,那就是要找结论了</p><p>令前半部分和后半部分差的绝对值为S,可以初步猜测M会希望增大S,而B则希望将其减少到0<br>在前半部分和后半部分填数一种使S增加0到9,记为n1,一种减少0到9,记为n2<br>我们做 n1 和 n2 的差的绝对值,记为N,因为M有办法使两者公共的个数不发挥任何作用(他只要不停抵消掉B做出的变化即可),所以这部分可以不考虑了,剩下的就是差<br>这部分M和B分别填N/2个数,如果N <em> 9 / 2大于或小于S,那么M可以通过不停填9或不停填0使自己胜出<br>所以B获胜的机会只有$N </em> 9 / 2 = S$时<br>可以证明这种情况他是必胜的,只要让每回合两人填的数和为9即可</p><p>然后通过这样的性质,直接判断即可</p><h2><span id="e-marbles">E. Marbles</span></h2><p>颜色范围非常小,考虑指数做法<br>直接暴力枚举颜色的排列是$O(n!)$的,考虑状压dp优化到$O(2^n)$<br>$f[s]$表示S这个集合的颜色已经分配过了,最小的交换次数,枚举新加入的颜色,这个颜色放在他们的后面,因此需要快速计算将一个颜色移到某些颜色的后面(前面)最小的交换次数,这样就可以快速转移了</p><p>记$t[i][j] $表示将i颜色移到j颜色前面最小的次数,此处不考虑其他颜色,对于每个i这个可以$O(n)$算出来,扫一遍维护一下后面还剩几个i即可</p><h2><span id="f-radio-stations">F. Radio Stations</span></h2><p>如果不考虑$f$,便是一个经典的2-sat问题,但所有选择的站点对应的区间应有交集,如果暴力枚举$f$的取值会变成$O(n^2)$</p><p>题解给出了一个巧妙地想法</p><p>区间限制其实也是也是限制,也具有逻辑关系,或许可以加入2-sat的建图中一起解决<br>考虑另外添加$ 2\times(M+1) $个点,分别表示$f$是否大于等于$i$<br>这样如果某个点选了,则$f$大于等于左端点为真,$f$大于等于右端点加一为假,连两条边,逆否命题再连两条边,共4条<br>要注意若大于等于$i$成立,则大于等于$i-1$也成立,逆否命题亦然,因此还要这样横着连一排边,共$2M$条<br>然后2-sat裸题,跑点双构造可行解即可<br><a id="more"></a></p><h1><span id="code">Code</span></h1><h2><span id="d">D</span></h2><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> rep(i, a, b) for(int i(a), i##_ORZ(b); i <= i##_ORZ; i++)</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> drep(i, a, b) for(int i(a), i##_ORZ(b); i >= i##_ORZ; i--)</span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> maxn = <span class="number">200010</span>;</span><br><span class="line"><span class="keyword">int</span> n, sum, l, r;</span><br><span class="line"><span class="keyword">char</span> str[maxn];</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%d"</span>, &n);</span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%s"</span>, str + <span class="number">1</span>);</span><br><span class="line">rep(i, <span class="number">1</span>, n / <span class="number">2</span>) <span class="keyword">if</span>(str[i] != <span class="string">'?'</span>) sum += str[i] - <span class="string">'0'</span>; <span class="keyword">else</span> l++;</span><br><span class="line">rep(i, n / <span class="number">2</span> + <span class="number">1</span>, n) <span class="keyword">if</span>(str[i] != <span class="string">'?'</span>) sum -= str[i] - <span class="string">'0'</span>; <span class="keyword">else</span> r++;</span><br><span class="line"><span class="keyword">if</span>(sum < <span class="number">0</span>) sum = -sum, swap(l, r);</span><br><span class="line">r -= l;</span><br><span class="line"><span class="keyword">if</span>(r / <span class="number">2</span> * <span class="number">9</span> == sum) <span class="built_in">printf</span>(<span class="string">"Bicarp\n"</span>);</span><br><span class="line"><span class="keyword">else</span> <span class="built_in">printf</span>(<span class="string">"Monocarp\n"</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2><span id="e">E</span></h2><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> rep(i, a, b) for(int i(a), i##_ORZ(b); i <= i##_ORZ; i++)</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> drep(i, a, b) for(int i(a), i##_ORZ(b); i >= i##_ORZ; i--)</span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> maxn = <span class="number">400010</span>;</span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">long</span> <span class="keyword">long</span> ll;</span><br><span class="line"><span class="keyword">int</span> n, a[maxn], c[maxn], cnt;</span><br><span class="line"><span class="keyword">long</span> <span class="keyword">long</span> f[<span class="number">30</span>][<span class="number">30</span>], g[<span class="number">1</span> << <span class="number">22</span>];</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">calc</span><span class="params">(<span class="keyword">int</span> x)</span> </span>{</span><br><span class="line"><span class="keyword">int</span> num = <span class="number">0</span>;</span><br><span class="line">rep(i, <span class="number">1</span>, n) <span class="keyword">if</span>(a[i] == x) num++;</span><br><span class="line">rep(i, <span class="number">1</span>, n) {</span><br><span class="line"><span class="keyword">if</span>(a[i] == x) num--;</span><br><span class="line"><span class="keyword">else</span> f[x][a[i]] += num;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%d"</span>, &n);</span><br><span class="line">rep(i, <span class="number">1</span>, n) <span class="built_in">scanf</span>(<span class="string">"%d"</span>, &a[i]), c[++cnt] = a[i];</span><br><span class="line">sort(c + <span class="number">1</span>, c + <span class="number">1</span> + cnt);</span><br><span class="line">cnt = unique(c + <span class="number">1</span>, c + <span class="number">1</span> + cnt) - c - <span class="number">1</span>;</span><br><span class="line">rep(i, <span class="number">1</span>, n) a[i] = lower_bound(c + <span class="number">1</span>, c + <span class="number">1</span> + cnt, a[i]) - c;</span><br><span class="line">rep(i, <span class="number">1</span>, cnt) calc(i); </span><br><span class="line"><span class="comment">//cout << cnt << endl;</span></span><br><span class="line"><span class="comment">//rep(i, 1, n) cout << a[i] << ' '; cout << endl;</span></span><br><span class="line"><span class="comment">//rep(i, 1, cnt) {</span></span><br><span class="line"><span class="comment">//rep(j, 1, cnt) cout << f[i][j] << ' '; cout << endl;</span></span><br><span class="line"><span class="comment">//}</span></span><br><span class="line"><span class="built_in">memset</span>(g, <span class="number">0x3f</span>, <span class="keyword">sizeof</span>(g));</span><br><span class="line">g[<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line">rep(s, <span class="number">0</span>, (<span class="number">1</span> << cnt) - <span class="number">1</span>) {</span><br><span class="line">rep(i, <span class="number">1</span>, cnt) <span class="keyword">if</span>(!(s & (<span class="number">1</span> << (i - <span class="number">1</span>)))) {</span><br><span class="line"><span class="keyword">int</span> ns = s + (<span class="number">1</span> << (i - <span class="number">1</span>));</span><br><span class="line"><span class="keyword">long</span> <span class="keyword">long</span> sum = <span class="number">0</span>;</span><br><span class="line">rep(j, <span class="number">1</span>, cnt) <span class="keyword">if</span>(s & (<span class="number">1</span> << (j - <span class="number">1</span>))) sum += f[j][i];</span><br><span class="line">g[ns] = min(g[ns], g[s] + sum);</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%lld\n"</span>, g[(<span class="number">1</span> << cnt) - <span class="number">1</span>]);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2><span id="f">F</span></h2><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> rep(i, a, b) for(int i(a), i##_ORZ(b); i <= i##_ORZ; i++)</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> drep(i, a, b) for(int i(a), i##_ORZ(b); i >= i##_ORZ; i--)</span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> maxn = <span class="number">1600010</span>;</span><br><span class="line"><span class="keyword">bool</span> b1;</span><br><span class="line"><span class="keyword">int</span> n, m1, m2, M, head[maxn], ver[maxn * <span class="number">4</span>], Next[maxn * <span class="number">4</span>], tot, N, v[maxn];</span><br><span class="line"><span class="keyword">int</span> dfn[maxn], low[maxn], bel[maxn], ins[maxn], sta[maxn], top, scc, num;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">add</span><span class="params">(<span class="keyword">int</span> x, <span class="keyword">int</span> y)</span> </span>{</span><br><span class="line">ver[++tot] = y, Next[tot] = head[x], head[x] = tot;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">tarjan</span><span class="params">(<span class="keyword">int</span> x)</span> </span>{</span><br><span class="line">dfn[x] = low[x] = ++num;</span><br><span class="line">sta[++top] = x; ins[x] = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = head[x]; i; i = Next[i])</span><br><span class="line"><span class="keyword">if</span>(!dfn[ver[i]]) tarjan(ver[i]), low[x] = min(low[x], low[ver[i]]);</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span>(ins[ver[i]]) low[x] = min(low[x], low[ver[i]]);</span><br><span class="line"><span class="keyword">if</span>(dfn[x] == low[x]) {</span><br><span class="line">scc++; <span class="keyword">int</span> z = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">while</span>(z != x) z = sta[top--], ins[z] = <span class="number">0</span>, bel[z] = scc;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="keyword">bool</span> b2;</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"><span class="comment">//printf("%lf\n", (double)(&b1-&b2)/1024/1024);</span></span><br><span class="line"><span class="keyword">int</span> x, y;</span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%d%d%d%d"</span>, &m1, &n, &M, &m2);</span><br><span class="line">N = n + M + <span class="number">1</span>;</span><br><span class="line">rep(i, <span class="number">1</span>, m1) <span class="built_in">scanf</span>(<span class="string">"%d%d"</span>, &x, &y), add(x + N, y), add(y + N, x);</span><br><span class="line">rep(i, <span class="number">1</span>, n) {</span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%d%d"</span>, &x, &y);</span><br><span class="line">add(i, x + n), add(i, y + <span class="number">1</span> + n + N);</span><br><span class="line">add(x + n + N, i + N), add(y + <span class="number">1</span> + n, i + N);</span><br><span class="line">}</span><br><span class="line">rep(i, <span class="number">1</span>, M) add(i + n + <span class="number">1</span>, i + n), add(i + n + N, i + n + N + <span class="number">1</span>);</span><br><span class="line">rep(i, <span class="number">1</span>, m2) <span class="built_in">scanf</span>(<span class="string">"%d%d"</span>, &x, &y), add(x, y + N), add(y, x + N);</span><br><span class="line">rep(i, <span class="number">1</span>, N + N) <span class="keyword">if</span>(!dfn[i]) tarjan(i);</span><br><span class="line">rep(i, <span class="number">1</span>, N) {</span><br><span class="line"><span class="keyword">if</span>(bel[i] == bel[i + N]) { <span class="built_in">printf</span>(<span class="string">"-1\n"</span>); <span class="keyword">return</span> <span class="number">0</span>; }</span><br><span class="line">v[i] = (bel[i] < bel[i + N]);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">int</span> k = <span class="number">0</span>, F = <span class="number">0</span>;;</span><br><span class="line">rep(i, <span class="number">1</span>, n) <span class="keyword">if</span>(v[i]) k++;</span><br><span class="line">rep(i, <span class="number">1</span>, M) <span class="keyword">if</span>(v[i + n]) F = i;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%d %d\n"</span>, k, F);</span><br><span class="line">rep(i, <span class="number">1</span>, n) <span class="keyword">if</span>(v[i]) <span class="built_in">printf</span>(<span class="string">"%d "</span>, i);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"\n"</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>题目链接:<a href="https://codeforces.com/contest/1215" target="_blank" rel="noopener">CF585</a></p>
</blockquote>
<h1 id="Solution"><a href="#Solution" class="headerlink" title="Solution"></a>Solution</h1><h2 id="D-Ticket-Game"><a href="#D-Ticket-Game" class="headerlink" title="D. Ticket Game"></a>D. Ticket Game</h2><p>无法直接SG函数的博弈论,那就是要找结论了</p>
<p>令前半部分和后半部分差的绝对值为S,可以初步猜测M会希望增大S,而B则希望将其减少到0<br>在前半部分和后半部分填数一种使S增加0到9,记为n1,一种减少0到9,记为n2<br>我们做 n1 和 n2 的差的绝对值,记为N,因为M有办法使两者公共的个数不发挥任何作用(他只要不停抵消掉B做出的变化即可),所以这部分可以不考虑了,剩下的就是差<br>这部分M和B分别填N/2个数,如果N <em> 9 / 2大于或小于S,那么M可以通过不停填9或不停填0使自己胜出<br>所以B获胜的机会只有$N </em> 9 / 2 = S$时<br>可以证明这种情况他是必胜的,只要让每回合两人填的数和为9即可</p>
<p>然后通过这样的性质,直接判断即可</p>
<h2 id="E-Marbles"><a href="#E-Marbles" class="headerlink" title="E. Marbles"></a>E. Marbles</h2><p>颜色范围非常小,考虑指数做法<br>直接暴力枚举颜色的排列是$O(n!)$的,考虑状压dp优化到$O(2^n)$<br>$f[s]$表示S这个集合的颜色已经分配过了,最小的交换次数,枚举新加入的颜色,这个颜色放在他们的后面,因此需要快速计算将一个颜色移到某些颜色的后面(前面)最小的交换次数,这样就可以快速转移了</p>
<p>记$t[i][j] $表示将i颜色移到j颜色前面最小的次数,此处不考虑其他颜色,对于每个i这个可以$O(n)$算出来,扫一遍维护一下后面还剩几个i即可</p>
<h2 id="F-Radio-Stations"><a href="#F-Radio-Stations" class="headerlink" title="F. Radio Stations"></a>F. Radio Stations</h2><p>如果不考虑$f$,便是一个经典的2-sat问题,但所有选择的站点对应的区间应有交集,如果暴力枚举$f$的取值会变成$O(n^2)$</p>
<p>题解给出了一个巧妙地想法</p>
<p>区间限制其实也是也是限制,也具有逻辑关系,或许可以加入2-sat的建图中一起解决<br>考虑另外添加$ 2\times(M+1) $个点,分别表示$f$是否大于等于$i$<br>这样如果某个点选了,则$f$大于等于左端点为真,$f$大于等于右端点加一为假,连两条边,逆否命题再连两条边,共4条<br>要注意若大于等于$i$成立,则大于等于$i-1$也成立,逆否命题亦然,因此还要这样横着连一排边,共$2M$条<br>然后2-sat裸题,跑点双构造可行解即可<br>
</summary>
<category term="图论" scheme="http://rentingxutx.github.io/categories/%E5%9B%BE%E8%AE%BA/"/>
<category term="动态规划" scheme="http://rentingxutx.github.io/categories/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"/>
<category term="2-sat" scheme="http://rentingxutx.github.io/categories/%E5%9B%BE%E8%AE%BA/2-sat/"/>
<category term="codeforces" scheme="http://rentingxutx.github.io/categories/codeforces/"/>
<category term="状压dp" scheme="http://rentingxutx.github.io/categories/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/%E7%8A%B6%E5%8E%8Bdp/"/>
<category term="2-sat" scheme="http://rentingxutx.github.io/tags/2-sat/"/>
<category term="状压dp" scheme="http://rentingxutx.github.io/tags/%E7%8A%B6%E5%8E%8Bdp/"/>
</entry>
<entry>
<title>BZOJ1562 [NOI2009]变换序列</title>
<link href="http://rentingxutx.github.io/2019/09/11/BZOJ1562-NOI2009-%E5%8F%98%E6%8D%A2%E5%BA%8F%E5%88%97/"/>
<id>http://rentingxutx.github.io/2019/09/11/BZOJ1562-NOI2009-变换序列/</id>
<published>2019-09-11T12:12:15.000Z</published>
<updated>2019-09-11T12:39:36.262Z</updated>
<content type="html"><![CDATA[<blockquote><p>题目链接:<a href="https://www.lydsy.com/JudgeOnline/problem.php?id=1562" target="_blank" rel="noopener">BZOJ1562</a></p></blockquote><h1><span id="solution">Solution</span></h1><p>题意明显是一个二分图匹配,左部匹配这个位置在新的排列中的数<br>给定了距离,那么该位置变换后可能的数只有两个,连两条边<br>之后就是求字典序最小的二分图完备匹配<br>大神对此有所研究啊,<a href="https://www.byvoid.com/zhs/blog/noi-2009-transform" target="_blank" rel="noopener">博客</a><br>第一想法是每个点贪心地选序号最小的点匹配,但这样匹配到后面无法直接匹配时会沿着增广路修改前面的匹配,于是就无法保证最优了<br>问题在于后面有权利修改前面使自己更优,违背了字典序的原则<br>但是如果倒过来,从后往前匹配,每次仍是贪心地尽可能匹配序号最小的点,这样就对了<br>因为变成了前面有权利修改后面使自己更优,所以永远是紧着前面的点的需要,贪心就对了<br>所以就是把边排一下序,然后跑匈牙利匹配,$O(n^2)$</p><p>上面那个博客里有$O(n)$的做法<br>找出刚才的性质之后就不必拘泥于跑模板了<br>考虑不再环里的边直接匹配即可<br>在环里的,我们已有贪心策略,标号最小的点先匹配,然后会有一些连锁反应,dfs下去,同样先走编号小的点,就可以了<br><a id="more"></a></p><h1><span id="code">Code</span></h1><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> rep(i, a, b) for(int i(a), i##_AWSL(b); i <= i##_AWSL; i++)</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> drep(i, a, b) for(int i(a), i##_AWSL(b); i >= i##_AWSL; i--)</span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> maxn = <span class="number">10010</span>;</span><br><span class="line"><span class="keyword">int</span> n, D[maxn], v[maxn * <span class="number">2</span>], mat[maxn * <span class="number">2</span>], tim;</span><br><span class="line"><span class="built_in">vector</span><<span class="keyword">int</span>> E[maxn * <span class="number">2</span>];</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">add</span><span class="params">(<span class="keyword">int</span> x, <span class="keyword">int</span> y)</span> </span>{ E[x].push_back(y); E[y].push_back(x); }</span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">dfs</span><span class="params">(<span class="keyword">int</span> x)</span> </span>{</span><br><span class="line">rep(i, <span class="number">0</span>, E[x].size() - <span class="number">1</span>) <span class="keyword">if</span>(v[E[x][i]] != tim) {</span><br><span class="line">v[E[x][i]] = tim;</span><br><span class="line"><span class="keyword">if</span>(!mat[E[x][i]] || dfs(mat[E[x][i]])) <span class="keyword">return</span> mat[x] = E[x][i], mat[E[x][i]] = x, <span class="literal">true</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%d"</span>, &n);</span><br><span class="line">rep(i, <span class="number">0</span>, n - <span class="number">1</span>) <span class="built_in">scanf</span>(<span class="string">"%d"</span>, &D[i]);</span><br><span class="line">rep(i, <span class="number">0</span>, n - <span class="number">1</span>) {</span><br><span class="line"><span class="keyword">if</span>(D[i] * <span class="number">2</span> > n) { <span class="built_in">printf</span>(<span class="string">"No Answer\n"</span>); <span class="keyword">return</span> <span class="number">0</span>; }</span><br><span class="line">add(i, (i + D[i]) % n + n); add((i + D[i]) % n + n, i);</span><br><span class="line">add(i, (i + n - D[i]) % n + n); add((i + n - D[i]) % n + n, i);</span><br><span class="line">}</span><br><span class="line">rep(i, <span class="number">0</span>, n * <span class="number">2</span> - <span class="number">1</span>) sort(E[i].begin(), E[i].end());</span><br><span class="line">drep(i, n - <span class="number">1</span>, <span class="number">0</span>) {</span><br><span class="line">tim = i + <span class="number">1</span>;</span><br><span class="line"><span class="keyword">if</span>(!dfs(i)) { <span class="built_in">printf</span>(<span class="string">"No Answer\n"</span>); <span class="keyword">return</span> <span class="number">0</span>; }</span><br><span class="line">}</span><br><span class="line">rep(i, <span class="number">0</span>, n - <span class="number">1</span>) <span class="built_in">printf</span>(<span class="string">"%d "</span>, mat[i] - n); <span class="built_in">printf</span>(<span class="string">"\n"</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>题目链接:<a href="https://www.lydsy.com/JudgeOnline/problem.php?id=1562" target="_blank" rel="noopener">BZOJ1562</a></p>
</blockquote>
<h1 id="Solution"><a href="#Solution" class="headerlink" title="Solution"></a>Solution</h1><p>题意明显是一个二分图匹配,左部匹配这个位置在新的排列中的数<br>给定了距离,那么该位置变换后可能的数只有两个,连两条边<br>之后就是求字典序最小的二分图完备匹配<br>大神对此有所研究啊,<a href="https://www.byvoid.com/zhs/blog/noi-2009-transform" target="_blank" rel="noopener">博客</a><br>第一想法是每个点贪心地选序号最小的点匹配,但这样匹配到后面无法直接匹配时会沿着增广路修改前面的匹配,于是就无法保证最优了<br>问题在于后面有权利修改前面使自己更优,违背了字典序的原则<br>但是如果倒过来,从后往前匹配,每次仍是贪心地尽可能匹配序号最小的点,这样就对了<br>因为变成了前面有权利修改后面使自己更优,所以永远是紧着前面的点的需要,贪心就对了<br>所以就是把边排一下序,然后跑匈牙利匹配,$O(n^2)$</p>
<p>上面那个博客里有$O(n)$的做法<br>找出刚才的性质之后就不必拘泥于跑模板了<br>考虑不再环里的边直接匹配即可<br>在环里的,我们已有贪心策略,标号最小的点先匹配,然后会有一些连锁反应,dfs下去,同样先走编号小的点,就可以了<br>
</summary>
<category term="图论" scheme="http://rentingxutx.github.io/categories/%E5%9B%BE%E8%AE%BA/"/>
<category term="二分图匹配" scheme="http://rentingxutx.github.io/categories/%E5%9B%BE%E8%AE%BA/%E4%BA%8C%E5%88%86%E5%9B%BE%E5%8C%B9%E9%85%8D/"/>
<category term="最小字典序二分图匹配" scheme="http://rentingxutx.github.io/tags/%E6%9C%80%E5%B0%8F%E5%AD%97%E5%85%B8%E5%BA%8F%E4%BA%8C%E5%88%86%E5%9B%BE%E5%8C%B9%E9%85%8D/"/>
</entry>
<entry>
<title>BZOJ1212 [HNOI2004]L语言</title>
<link href="http://rentingxutx.github.io/2019/09/11/BZOJ1212-HNOI2004-L%E8%AF%AD%E8%A8%80/"/>
<id>http://rentingxutx.github.io/2019/09/11/BZOJ1212-HNOI2004-L语言/</id>
<published>2019-09-11T12:02:46.000Z</published>
<updated>2019-09-11T12:11:15.403Z</updated>
<content type="html"><![CDATA[<blockquote><p>题目链接:<a href="https://www.lydsy.com/JudgeOnline/problem.php?id=1212" target="_blank" rel="noopener">BZOJ1212</a></p></blockquote><h1><span id="solution">Solution</span></h1><p>我最开始竟然往AC自动机上想。。。<br>实际上就是一个1D1D的dp<br>如果一个前缀可以,那么在后面接一个单词后形成的前缀仍然可以<br>用trie树加速接单词的过程,把所有单词建成trie,用文章在上面走,走到一个endpos该位置就可以,dp即可<br><a id="more"></a></p><h1><span id="code">Code</span></h1><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> maxn = <span class="number">2000010</span>;</span><br><span class="line"><span class="keyword">int</span> n, m, ch[<span class="number">210</span>][<span class="number">27</span>], epos[<span class="number">210</span>], tot = <span class="number">1</span>, f[maxn];</span><br><span class="line"><span class="keyword">char</span> str[maxn];</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">insert</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> u = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="built_in">strlen</span>(str); i++) {</span><br><span class="line"> <span class="keyword">int</span> c = str[i] - <span class="string">'a'</span>;</span><br><span class="line"> <span class="keyword">if</span>(!ch[u][c]) ch[u][c] = ++tot;</span><br><span class="line"> u = ch[u][c];</span><br><span class="line"> }</span><br><span class="line"> epos[u] = <span class="number">1</span>;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="built_in">scanf</span>(<span class="string">"%d%d"</span>, &n, &m);</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i++) <span class="built_in">scanf</span>(<span class="string">"%s"</span>, str), insert();</span><br><span class="line"> <span class="keyword">while</span>(m--) {</span><br><span class="line"> <span class="built_in">scanf</span>(<span class="string">"%s"</span>, str + <span class="number">1</span>); n = <span class="built_in">strlen</span>(str + <span class="number">1</span>);</span><br><span class="line"> <span class="built_in">memset</span>(f, <span class="number">0</span>, <span class="keyword">sizeof</span>(f));</span><br><span class="line"> f[<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i < n; i++) <span class="keyword">if</span>(f[i]) {</span><br><span class="line"> <span class="keyword">int</span> u = <span class="number">1</span>; </span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> j = i + <span class="number">1</span>; j <= n; j++) {</span><br><span class="line"> <span class="keyword">int</span> c = str[j] - <span class="string">'a'</span>;</span><br><span class="line"> <span class="keyword">if</span>(!ch[u][c]) <span class="keyword">break</span>;</span><br><span class="line"> u = ch[u][c];</span><br><span class="line"> <span class="keyword">if</span>(epos[u]) f[j] = <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i = n; i >= <span class="number">0</span>; i--) <span class="keyword">if</span>(f[i]) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%d\n"</span>, i);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>题目链接:<a href="https://www.lydsy.com/JudgeOnline/problem.php?id=1212" target="_blank" rel="noopener">BZOJ1212</a></p>
</blockquote>
<h1 id="Solution"><a href="#Solution" class="headerlink" title="Solution"></a>Solution</h1><p>我最开始竟然往AC自动机上想。。。<br>实际上就是一个1D1D的dp<br>如果一个前缀可以,那么在后面接一个单词后形成的前缀仍然可以<br>用trie树加速接单词的过程,把所有单词建成trie,用文章在上面走,走到一个endpos该位置就可以,dp即可<br>
</summary>
<category term="动态规划" scheme="http://rentingxutx.github.io/categories/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"/>
<category term="数据结构优化" scheme="http://rentingxutx.github.io/categories/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%BC%98%E5%8C%96/"/>
<category term="字符串" scheme="http://rentingxutx.github.io/categories/%E5%AD%97%E7%AC%A6%E4%B8%B2/"/>
<category term="Trie" scheme="http://rentingxutx.github.io/categories/%E5%AD%97%E7%AC%A6%E4%B8%B2/Trie/"/>
<category term="Trie" scheme="http://rentingxutx.github.io/tags/Trie/"/>
<category term="动态规划" scheme="http://rentingxutx.github.io/tags/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"/>
</entry>
<entry>
<title>BZOJ1145 [CTSC2008]图腾totem</title>
<link href="http://rentingxutx.github.io/2019/09/10/BZOJ1145-CTSC2008-%E5%9B%BE%E8%85%BEtotem/"/>
<id>http://rentingxutx.github.io/2019/09/10/BZOJ1145-CTSC2008-图腾totem/</id>
<published>2019-09-10T10:54:55.000Z</published>
<updated>2019-09-11T11:48:03.578Z</updated>
<content type="html"><![CDATA[<blockquote><p>题目链接:<a href="https://www.lydsy.com/JudgeOnline/problem.php?id=1145" target="_blank" rel="noopener">BZOJ1145</a></p></blockquote><h1><span id="solution">Solution</span></h1><p>用1234的一个排列来表示一个图形4个点的相对高度,x表示待定<br>ans<br>= 1324 - 1243 - 1432<br>= 1x2x - 1423 - (12xx - 1234) - (14xx - 1423)<br>= 1x2x + 1234 - 12xx - 14xx<br>= 1x2x + 1234 - 1xxx + 13xx<br>这样看起来就比较可求了</p><p>先处理出每个点的左边和右边比它低的点分别有几个,记为$ l[i] $和$ r[i] $,用树状数组</p><h2><span id="1234">1234</span></h2><p>枚举3的位置i,4的个数该位置左边比它大的个数,也就是$n - i - r[i]$,12的对数是所有左边小于它的点的$l$的和,用树状数组维护$l$前缀和,4和12相乘即可</p><h2><span id="1xxx">1xxx</span></h2><p>设当前点后面比他大的个数是$ t = n - i - r[i] $,则答案为$ \frac{t\times (t-1)\times (t-2)}{6} $</p><h2><span id="1x2x">1x2x</span></h2><p>枚举2的位置i,后面的个数是$n - i - r[i]$,对于前面,先统计所有$x < i, a[x] < a[i]$,y任意的数对个数$\sum l[j] * (i - 1)$,然后要去掉$y < x$或$a[y] < a[i], y > x$,前者即为$\sum j (a[j] < a[i]) $,用树状数组维护,后者为$ \frac{l[i] \times (l[i] - 1)}{2} $</p><h2><span id="13xx">13xx</span></h2><p>把这种情况转90度,似乎就是1x2x了,然后用相似的方法算即可</p><p>finished</p><p>我只想说,这种做法是怎么想到的啊!</p><a id="more"></a><h1><span id="code">Code</span></h1><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> maxn = <span class="number">200010</span>, mod = <span class="number">16777216</span>;</span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">long</span> <span class="keyword">long</span> ll;</span><br><span class="line"><span class="function">ll <span class="title">read</span><span class="params">()</span> </span>{</span><br><span class="line">ll res = <span class="number">0</span>, f = <span class="number">1</span>; <span class="keyword">char</span> c = getchar(); </span><br><span class="line"><span class="keyword">while</span>(c != <span class="string">'-'</span> && (c < <span class="string">'0'</span> || c > <span class="string">'9'</span>)) c = getchar(); <span class="keyword">if</span>(c == <span class="string">'-'</span>) f = <span class="number">-1</span>, c = getchar();</span><br><span class="line"><span class="keyword">while</span>(c >= <span class="string">'0'</span> && c <= <span class="string">'9'</span>) res = (res << <span class="number">3</span>) + (res << <span class="number">1</span>) + c - <span class="string">'0'</span>, c = getchar(); <span class="keyword">return</span> res * f;</span><br><span class="line">}</span><br><span class="line">ll a[maxn], l[maxn], r[maxn], c[maxn], ans, n;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">add</span><span class="params">(<span class="keyword">int</span> p, <span class="keyword">int</span> x)</span> </span>{</span><br><span class="line"><span class="keyword">for</span>(; p <= n; p += p&-p) c[p] = (c[p] + x) % mod;</span><br><span class="line">}</span><br><span class="line"><span class="function">ll <span class="title">query</span><span class="params">(<span class="keyword">int</span> p)</span> </span>{</span><br><span class="line">ll res = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span>(; p; p -= p&-p) res = (res + c[p]) % mod;</span><br><span class="line"><span class="keyword">return</span> res;</span><br><span class="line">}</span><br><span class="line"><span class="function">ll <span class="title">c_1x2x</span><span class="params">()</span> </span>{</span><br><span class="line">ll res = <span class="number">0</span>; <span class="built_in">memset</span>(c, <span class="number">0</span>, <span class="keyword">sizeof</span>(c));</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i++) {</span><br><span class="line">res += (n - i - r[i]) * (l[i] * (i - <span class="number">1</span>) - (l[i] - <span class="number">1</span>) * l[i] / <span class="number">2</span> - query(a[i])) % mod;</span><br><span class="line">res %= mod;</span><br><span class="line">add(a[i], i);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> res;</span><br><span class="line">}</span><br><span class="line"><span class="function">ll <span class="title">c_13xx</span><span class="params">()</span> </span>{</span><br><span class="line">ll res = <span class="number">0</span>; <span class="built_in">memset</span>(c, <span class="number">0</span>, <span class="keyword">sizeof</span>(c));</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i++) {</span><br><span class="line">res += (n - i - r[i]) * (l[i] * (a[i] - <span class="number">1</span>) - query(a[i]) - l[i] * (l[i] - <span class="number">1</span>) / <span class="number">2</span>) % mod;</span><br><span class="line">res %= mod;</span><br><span class="line">add(a[i], a[i]);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> res;</span><br><span class="line">}</span><br><span class="line"><span class="function">ll <span class="title">c_1xxx</span><span class="params">()</span> </span>{</span><br><span class="line">ll res = <span class="number">0</span>, t;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i++) t = n - i - r[i], res = (res + t * (t - <span class="number">1</span>) * (t - <span class="number">2</span>) / <span class="number">6</span>) % mod;</span><br><span class="line"><span class="keyword">return</span> res;</span><br><span class="line">}</span><br><span class="line"><span class="function">ll <span class="title">c_1234</span><span class="params">()</span> </span>{</span><br><span class="line"><span class="built_in">memset</span>(c, <span class="number">0</span>, <span class="keyword">sizeof</span>(c)); ll res = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i++) {</span><br><span class="line">res = (res + (n - i - r[i]) * query(a[i]) % mod) % mod;</span><br><span class="line">add(a[i], l[i]);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> res;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">freopen(<span class="string">"totem.in"</span>, <span class="string">"r"</span>, <span class="built_in">stdin</span>);</span><br><span class="line">freopen(<span class="string">"totem.out"</span>, <span class="string">"w"</span>, <span class="built_in">stdout</span>);</span><br><span class="line">n = read();</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i++) a[i] = read();</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i++) l[i] = query(a[i]), r[i] = a[i] - <span class="number">1</span> - l[i], add(a[i], <span class="number">1</span>);</span><br><span class="line">ans = (c_1x2x() + c_13xx() - c_1xxx() + c_1234() + mod) % mod;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%lld\n"</span>, ans);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>题目链接:<a href="https://www.lydsy.com/JudgeOnline/problem.php?id=1145" target="_blank" rel="noopener">BZOJ1145</a></p>
</blockquote>
<h1 id="Solution"><a href="#Solution" class="headerlink" title="Solution"></a>Solution</h1><p>用1234的一个排列来表示一个图形4个点的相对高度,x表示待定<br>ans<br>= 1324 - 1243 - 1432<br>= 1x2x - 1423 - (12xx - 1234) - (14xx - 1423)<br>= 1x2x + 1234 - 12xx - 14xx<br>= 1x2x + 1234 - 1xxx + 13xx<br>这样看起来就比较可求了</p>
<p>先处理出每个点的左边和右边比它低的点分别有几个,记为$ l[i] $和$ r[i] $,用树状数组</p>
<h2 id="1234"><a href="#1234" class="headerlink" title="1234"></a>1234</h2><p>枚举3的位置i,4的个数该位置左边比它大的个数,也就是$n - i - r[i]$,12的对数是所有左边小于它的点的$l$的和,用树状数组维护$l$前缀和,4和12相乘即可</p>
<h2 id="1xxx"><a href="#1xxx" class="headerlink" title="1xxx"></a>1xxx</h2><p>设当前点后面比他大的个数是$ t = n - i - r[i] $,则答案为$ \frac{t\times (t-1)\times (t-2)}{6} $</p>
<h2 id="1x2x"><a href="#1x2x" class="headerlink" title="1x2x"></a>1x2x</h2><p>枚举2的位置i,后面的个数是$n - i - r[i]$,对于前面,先统计所有$x &lt; i, a[x] &lt; a[i]$,y任意的数对个数$\sum l[j] * (i - 1)$,然后要去掉$y &lt; x$或$a[y] &lt; a[i], y &gt; x$,前者即为$\sum j (a[j] &lt; a[i]) $,用树状数组维护,后者为$ \frac{l[i] \times (l[i] - 1)}{2} $</p>
<h2 id="13xx"><a href="#13xx" class="headerlink" title="13xx"></a>13xx</h2><p>把这种情况转90度,似乎就是1x2x了,然后用相似的方法算即可</p>
<p>finished</p>
<p>我只想说,这种做法是怎么想到的啊!</p>
</summary>
<category term="数据结构" scheme="http://rentingxutx.github.io/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/"/>
<category term="树状数组" scheme="http://rentingxutx.github.io/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E6%A0%91%E7%8A%B6%E6%95%B0%E7%BB%84/"/>
<category term="树状数组" scheme="http://rentingxutx.github.io/tags/%E6%A0%91%E7%8A%B6%E6%95%B0%E7%BB%84/"/>
</entry>
<entry>
<title>BZOJ1129 [POI2008]Per</title>
<link href="http://rentingxutx.github.io/2019/09/09/BZOJ1129-POI2008-Per/"/>
<id>http://rentingxutx.github.io/2019/09/09/BZOJ1129-POI2008-Per/</id>
<published>2019-09-09T12:09:08.000Z</published>
<updated>2019-09-09T12:56:59.652Z</updated>
<content type="html"><![CDATA[<blockquote><p>题目链接:<a href="https://www.lydsy.com/JudgeOnline/problem.php?id=1129" target="_blank" rel="noopener">BZOJ1129</a></p></blockquote><h1><span id="solution">Solution</span></h1><p>如果没有重复元素,直接一位一位计数即可<br>有相同元素仍然是一位一位计数,考虑从后往前,假设当前前i-1个是固定的,数在第i位开始小于给定的序列的方案数<br>设原数列为$a$,可以变动的中,不同的元素分别是$b_1, b_2, b_3, …, b_k$,个数分别为$c_1, c_2, c_3…, c_k$个,则答案为<br>$$<br>\sum_{b_j< a_i}\frac{(n-i)!}{\Pi_{k!=j} b_k\cdot(b_j-1) }<br>$$<br>硬算是$O(n^2)$的,可以用树状数组优化<br>分别维护分母和分子,每次i–时有一个新的元素加进来,某一个b会+1,分母会乘以新的b</p><p>关键是这个模数的问题,他不是质数,要质因数分解,分别求答案,然后用CRT合并<br>逆元需要用扩展欧几里得来算</p><p>还有比较麻烦的,如果乘以或除以的数是当前处理的模数对应的那个质因子的倍数,那么需要把这个数所有该因数除掉,同时记录去掉了几个这个因数<br>最后要把这些去掉的因数再快速幂乘回来<br>这是因为求逆元时要求与模数互质,因此必须先去掉,求完逆元再乘回来</p><p>任意模数的逆元的处理方式要记笔记。。。</p><a id="more"></a><h1><span id="code">Code</span></h1><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> maxn = <span class="number">300010</span>;</span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">long</span> <span class="keyword">long</span> ll;</span><br><span class="line"><span class="keyword">int</span> n, mod, a[maxn], m, pr[maxn], b[maxn], pb[maxn], cnt, v[maxn], mx;</span><br><span class="line">ll c[maxn], res[maxn];</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">exgcd</span><span class="params">(<span class="keyword">int</span> a, <span class="keyword">int</span> b, <span class="keyword">int</span> &x, <span class="keyword">int</span> &y)</span> </span>{</span><br><span class="line"><span class="keyword">if</span>(!b) { x = <span class="number">1</span>; y = <span class="number">0</span>; <span class="keyword">return</span> a; }</span><br><span class="line"><span class="keyword">int</span> d = exgcd(b, a % b, y, x);</span><br><span class="line">y -= a / b * x;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">inv</span><span class="params">(<span class="keyword">int</span> x, <span class="keyword">int</span> p)</span> </span>{</span><br><span class="line"><span class="keyword">int</span> r, y;</span><br><span class="line">exgcd(x, p, r, y);</span><br><span class="line"><span class="keyword">return</span> (r % p + p) % p;</span><br><span class="line">}</span><br><span class="line"><span class="function">ll <span class="title">query</span><span class="params">(<span class="keyword">int</span> p)</span> </span>{</span><br><span class="line">ll res = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span>(; p; p -= p&-p) res = (res + c[p]) % mod; </span><br><span class="line"><span class="keyword">return</span> res;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">add</span><span class="params">(<span class="keyword">int</span> p)</span> </span>{</span><br><span class="line"><span class="keyword">for</span>(; p <= mx; p += p&-p) c[p] = (c[p] + <span class="number">1</span>) % mod;</span><br><span class="line">}</span><br><span class="line">ll pwp[maxn];</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">solve</span><span class="params">(<span class="keyword">int</span> k)</span> </span>{</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i <= mx; i++) c[i] = <span class="number">0</span>, v[i] = <span class="number">0</span>;</span><br><span class="line">ll p1 = <span class="number">0</span>, a1 = <span class="number">1</span>, aa, pp;</span><br><span class="line">res[k] = <span class="number">1</span>;</span><br><span class="line">pwp[<span class="number">0</span>] = <span class="number">1</span>; </span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i++) pwp[i] = pwp[i<span class="number">-1</span>] * pr[k] % mod;</span><br><span class="line">v[a[n]]++; add(a[n]);</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = n - <span class="number">1</span>; i; i--) {</span><br><span class="line">v[a[i]]++; add(a[i]);</span><br><span class="line"><span class="keyword">for</span>(aa = n - i, pp = <span class="number">0</span>; aa % pr[k] == <span class="number">0</span>; aa /= pr[k], pp++);</span><br><span class="line">a1 = a1 * aa % mod; p1 += pp;</span><br><span class="line"><span class="keyword">for</span>(aa = v[a[i]], pp = <span class="number">0</span>; aa % pr[k] == <span class="number">0</span>; aa /= pr[k], pp++);</span><br><span class="line">a1 = a1 * inv(aa, mod) % mod; p1 -= pp;</span><br><span class="line">res[k] = (res[k] + a1 % mod * query(a[i] - <span class="number">1</span>) % mod * pwp[p1] % mod) % mod;</span><br><span class="line">}</span><br><span class="line"><span class="comment">// cout << k << ' ' << res[k] << endl;</span></span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="built_in">scanf</span>(<span class="string">"%d%d"</span>, &n, &m);</span><br><span class="line"> <span class="keyword">int</span> sm = m;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i++) <span class="built_in">scanf</span>(<span class="string">"%d"</span>, &a[i]), mx = max(mx, a[i]);</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">2</span>; i * i <= m; i++) <span class="keyword">if</span>(m % i == <span class="number">0</span>) {</span><br><span class="line">pr[++cnt] = i; pb[cnt] = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">while</span>(m % i == <span class="number">0</span>) m /= i, b[cnt]++, pb[cnt] *= pr[cnt];</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span>(m != <span class="number">1</span>) pr[++cnt] = m, b[cnt] = <span class="number">1</span>, pb[cnt] = m;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= cnt; i++) mod = pb[i], solve(i);</span><br><span class="line">ll ans = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= cnt; i++) {</span><br><span class="line"><span class="keyword">int</span> M = sm / pb[i];</span><br><span class="line">ans = (ans + res[i] * M % sm * inv(M, pb[i]) % sm) % sm;</span><br><span class="line">}</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%lld\n"</span>, ans);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>题目链接:<a href="https://www.lydsy.com/JudgeOnline/problem.php?id=1129" target="_blank" rel="noopener">BZOJ1129</a></p>
</blockquote>
<h1 id="Solution"><a href="#Solution" class="headerlink" title="Solution"></a>Solution</h1><p>如果没有重复元素,直接一位一位计数即可<br>有相同元素仍然是一位一位计数,考虑从后往前,假设当前前i-1个是固定的,数在第i位开始小于给定的序列的方案数<br>设原数列为$a$,可以变动的中,不同的元素分别是$b_1, b_2, b_3, …, b_k$,个数分别为$c_1, c_2, c_3…, c_k$个,则答案为<br>$$<br>\sum_{b_j&lt; a_i}\frac{(n-i)!}{\Pi_{k!=j} b_k\cdot(b_j-1) }<br>$$<br>硬算是$O(n^2)$的,可以用树状数组优化<br>分别维护分母和分子,每次i–时有一个新的元素加进来,某一个b会+1,分母会乘以新的b</p>
<p>关键是这个模数的问题,他不是质数,要质因数分解,分别求答案,然后用CRT合并<br>逆元需要用扩展欧几里得来算</p>
<p>还有比较麻烦的,如果乘以或除以的数是当前处理的模数对应的那个质因子的倍数,那么需要把这个数所有该因数除掉,同时记录去掉了几个这个因数<br>最后要把这些去掉的因数再快速幂乘回来<br>这是因为求逆元时要求与模数互质,因此必须先去掉,求完逆元再乘回来</p>
<p>任意模数的逆元的处理方式要记笔记。。。</p>
</summary>
<category term="数据结构" scheme="http://rentingxutx.github.io/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/"/>
<category term="树状数组" scheme="http://rentingxutx.github.io/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E6%A0%91%E7%8A%B6%E6%95%B0%E7%BB%84/"/>
<category term="数学" scheme="http://rentingxutx.github.io/categories/%E6%95%B0%E5%AD%A6/"/>
<category term="组合" scheme="http://rentingxutx.github.io/categories/%E6%95%B0%E5%AD%A6/%E7%BB%84%E5%90%88/"/>
<category term="组合数学" scheme="http://rentingxutx.github.io/tags/%E7%BB%84%E5%90%88%E6%95%B0%E5%AD%A6/"/>
<category term="树状数组" scheme="http://rentingxutx.github.io/tags/%E6%A0%91%E7%8A%B6%E6%95%B0%E7%BB%84/"/>
</entry>
<entry>
<title>BZOJ1113 [Poi2008]海报PLA</title>
<link href="http://rentingxutx.github.io/2019/09/09/BZOJ1113-Poi2008-%E6%B5%B7%E6%8A%A5PLA/"/>
<id>http://rentingxutx.github.io/2019/09/09/BZOJ1113-Poi2008-海报PLA/</id>
<published>2019-09-09T12:01:00.000Z</published>
<updated>2019-09-09T12:08:26.631Z</updated>
<content type="html"><![CDATA[<blockquote><p>题目链接:<a href="https://www.lydsy.com/JudgeOnline/problem.php?id=1113" target="_blank" rel="noopener">BZOJ1113</a></p></blockquote><h1><span id="solution">Solution</span></h1><p>海报个数减少的机会就是两个矩阵高度相同,且他们之间的矩阵都比他们高</p><p>这样用一个单调栈就可以了,维护一个上升的高度,每个矩阵只有在弹出一个和他高度相同的矩阵时才不增加答案。<br><a id="more"></a></p><h1><span id="code">Code</span></h1><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">int</span> n, sta[<span class="number">250010</span>], tp, ans;</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"><span class="keyword">int</span> x, y;</span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%d"</span>, &n);</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i++) {</span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%d%d"</span>, &x, &y);</span><br><span class="line"><span class="keyword">while</span>(tp && sta[tp] >= y) {</span><br><span class="line"><span class="keyword">if</span>(sta[tp] > y) ans++;</span><br><span class="line">tp--;</span><br><span class="line">}</span><br><span class="line">sta[++tp] = y;</span><br><span class="line">}</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%d\n"</span>, ans + tp);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>题目链接:<a href="https://www.lydsy.com/JudgeOnline/problem.php?id=1113" target="_blank" rel="noopener">BZOJ1113</a></p>
</blockquote>
<h1 id="Solution"><a href="#Solution" class="headerlink" title="Solution"></a>Solution</h1><p>海报个数减少的机会就是两个矩阵高度相同,且他们之间的矩阵都比他们高</p>
<p>这样用一个单调栈就可以了,维护一个上升的高度,每个矩阵只有在弹出一个和他高度相同的矩阵时才不增加答案。<br>
</summary>
<category term="数据结构" scheme="http://rentingxutx.github.io/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/"/>
<category term="单调栈" scheme="http://rentingxutx.github.io/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E5%8D%95%E8%B0%83%E6%A0%88/"/>
<category term="单调栈" scheme="http://rentingxutx.github.io/tags/%E5%8D%95%E8%B0%83%E6%A0%88/"/>
</entry>
<entry>
<title>BZOJ1098 [POI2007]办公楼biu</title>
<link href="http://rentingxutx.github.io/2019/09/09/BZOJ1098-POI2007-%E5%8A%9E%E5%85%AC%E6%A5%BCbiu/"/>
<id>http://rentingxutx.github.io/2019/09/09/BZOJ1098-POI2007-办公楼biu/</id>
<published>2019-09-09T11:32:40.000Z</published>
<updated>2019-09-09T11:53:31.143Z</updated>
<content type="html"><![CDATA[<blockquote><p>题目链接:<a href="https://www.lydsy.com/JudgeOnline/problem.php?id=1098" target="_blank" rel="noopener">BZOJ1098</a></p></blockquote><h1><span id="solution">Solution</span></h1><p>题意:将一个图的点分成尽可能多的集合,任意两个属于不同集合的点都有边相连</p><p>换句话说,求补图的联通块数</p><p>然而补图的边数是$ O(n^2) $的,连边都连不了,并查集不行啊<br>我们回归到原始,考虑bfs或dfs时需要做什么<br>其实就是在每个点的时候,快速找到所有与他相连的未标记的点。</p><blockquote><p>对于一个点,可以$O(n)$找到所有与他相连的点(废话)</p></blockquote><blockquote><p>如果使用bfs的话,每个点只需要被这样找到一次(不是废话)</p></blockquote><p>因为一个点被找到之后,就可以入队然后找他的连边的点了,之后就算他再一次被找到,他们也已经被标记在同一个联通块里了,所以再一次找到他一次就没有必要了<br>因此在找到一个点后,入队之余把他删除掉就好了,这个用一个链表就解决了</p><p>实质上相当于把每个连通块简化成了一个bfs树,所有其他边都是没有必要的,边数就是$O(n)$的,然后就很好搞了</p><a id="more"></a><h1><span id="code">Code</span></h1><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> maxn = <span class="number">100010</span>, maxm = <span class="number">4000010</span>;</span><br><span class="line"><span class="keyword">int</span> head[maxn], ver[maxm], Next[maxm], tot, n, m, v[maxn], nxt[maxn], pre[maxn], col[maxn], tim, dd[maxn], sz[maxn];</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">add</span><span class="params">(<span class="keyword">int</span> x, <span class="keyword">int</span> y)</span> </span>{ ver[++tot] = y, Next[tot] = head[x], head[x] = tot; }</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">del</span><span class="params">(<span class="keyword">int</span> x)</span> </span>{</span><br><span class="line">nxt[pre[x]] = nxt[x];</span><br><span class="line">pre[nxt[x]] = pre[x];</span><br><span class="line">dd[x] = <span class="number">1</span>;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">bfs</span><span class="params">(<span class="keyword">int</span> s)</span> </span>{</span><br><span class="line"><span class="built_in">queue</span><<span class="keyword">int</span>> q;</span><br><span class="line">q.push(s); col[s] = tim; del(s);</span><br><span class="line"><span class="keyword">while</span>(q.size()) {</span><br><span class="line"><span class="keyword">int</span> x = q.front(); q.pop();</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = nxt[<span class="number">0</span>]; i; i = nxt[i]) v[i] = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = head[x]; i; i = Next[i]) <span class="keyword">if</span>(!dd[ver[i]]) v[ver[i]] = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = nxt[<span class="number">0</span>]; i; i = nxt[i]) <span class="keyword">if</span>(!v[i] && !dd[i]) col[i] = tim, del(i), q.push(i);</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%d%d"</span>, &n, &m);</span><br><span class="line"><span class="keyword">int</span> x, y;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= m; i++) <span class="built_in">scanf</span>(<span class="string">"%d%d"</span>, &x, &y), add(x, y), add(y, x);</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i < n; i++) nxt[i] = i + <span class="number">1</span>;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i++) pre[i] = i - <span class="number">1</span>;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i++) <span class="keyword">if</span>(!col[i]) tim++, bfs(i);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%d\n"</span>, tim);</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i++) sz[col[i]]++;</span><br><span class="line">sort(sz + <span class="number">1</span>, sz + <span class="number">1</span> + tim);</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= tim; i++) <span class="built_in">printf</span>(<span class="string">"%d "</span>, sz[i]); <span class="built_in">printf</span>(<span class="string">"\n"</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>题目链接:<a href="https://www.lydsy.com/JudgeOnline/problem.php?id=1098" target="_blank" rel="noopener">BZOJ1098</a></p>
</blockquote>
<h1 id="Solution"><a href="#Solution" class="headerlink" title="Solution"></a>Solution</h1><p>题意:将一个图的点分成尽可能多的集合,任意两个属于不同集合的点都有边相连</p>
<p>换句话说,求补图的联通块数</p>
<p>然而补图的边数是$ O(n^2) $的,连边都连不了,并查集不行啊<br>我们回归到原始,考虑bfs或dfs时需要做什么<br>其实就是在每个点的时候,快速找到所有与他相连的未标记的点。</p>
<blockquote>
<p>对于一个点,可以$O(n)$找到所有与他相连的点(废话)</p>
</blockquote>
<blockquote>
<p>如果使用bfs的话,每个点只需要被这样找到一次(不是废话)</p>
</blockquote>
<p>因为一个点被找到之后,就可以入队然后找他的连边的点了,之后就算他再一次被找到,他们也已经被标记在同一个联通块里了,所以再一次找到他一次就没有必要了<br>因此在找到一个点后,入队之余把他删除掉就好了,这个用一个链表就解决了</p>
<p>实质上相当于把每个连通块简化成了一个bfs树,所有其他边都是没有必要的,边数就是$O(n)$的,然后就很好搞了</p>
</summary>
<category term="图论" scheme="http://rentingxutx.github.io/categories/%E5%9B%BE%E8%AE%BA/"/>
<category term="补图" scheme="http://rentingxutx.github.io/categories/%E5%9B%BE%E8%AE%BA/%E8%A1%A5%E5%9B%BE/"/>
<category term="搞" scheme="http://rentingxutx.github.io/tags/%E6%90%9E/"/>
<category term="bfs" scheme="http://rentingxutx.github.io/tags/bfs/"/>
<category term="链表" scheme="http://rentingxutx.github.io/tags/%E9%93%BE%E8%A1%A8/"/>
</entry>
<entry>
<title>BZOJ1065 [NOI2008]奥运物流</title>
<link href="http://rentingxutx.github.io/2019/08/31/BZOJ1065-NOI2008-%E5%A5%A5%E8%BF%90%E7%89%A9%E6%B5%81/"/>
<id>http://rentingxutx.github.io/2019/08/31/BZOJ1065-NOI2008-奥运物流/</id>
<published>2019-08-31T09:10:24.000Z</published>
<updated>2019-09-09T11:30:39.584Z</updated>
<content type="html"><![CDATA[<blockquote><p>题目链接:<a href="https://www.lydsy.com/JudgeOnline/problem.php?id=1065" target="_blank" rel="noopener">BZOJ1065</a></p></blockquote><h1><span id="solution">Solution</span></h1><p>有一颗有根树,根节点还有一个父亲,每个点的可靠性与儿子们相关。</p><p>考虑如果是一棵纯正的树,那么答案应为<br>$$<br>\sum_{i=1}^nk^{dep[i]}C_i<br>$$<br>当出现环后,也就出现了一个方程,解这个方程即可,设1号点的后继为t<br>$$<br>ans = \sum_{i=1}^nk^{dep[i]}C_i + ans\times k^{dep[x]} \\<br>ans = \frac{\sum_{i=1}^nk^{dep[i]}C_i}{1 - k^{dep[x]}}<br>$$<br>容易发现每个修改后继的点,后继一定修改成了1号节点,这样最优<br>枚举1号点的后继的深度,分母也就确定了,就可以不用考虑环了</p><p>于是就是在树上dp,用$f[i][j]$表示以i为根的子树中,j个点接到1的下面,最大的稳定度<br>但问题是转移时稳定度需要用深度来算啊,这样的状态无法转移<br>因此再加一维记录深度,用$f[i][j][k]$表示以i为根的子树中,i的深度为k,有j个点接到1的下面,最大的稳定度<br>这样转移时每个点u可以考虑是否接到1的下面</p><ol><li><p>如果u后继改为1,那么儿子的深度要么是1(也改了),要么是2(没改)<br>转移如下(设v是u的儿子)<br>$$<br>f[u][i][j] = C_u\times k + max(f[v][k][1], f[v][k][2])<br>$$<br>有限制条件,$\sum k = i - 1$,所以后面一项要用背包dp来做</p></li><li><p>如果u后继不改,那么儿子深度为$j + 1$或1<br>可以同理得到这样的转移<br>$$<br>f[u][i][j] = C_u \times k^j + max(f[v][k][1], f[v][k][j + 1])<br>$$<br>限制相同,用同样的背包dp做</p></li></ol><p>代码中背包dp部分的$g[i][j]$表示的是考虑前i个儿子,j个点后继改为1的最大值,实际上就是套路的背包dp</p><p>关于上面说的枚举1后继的深度,实际上可以在1的后继到1号点这条链上枚举每一个点,将他的后继改为1,dp更新答案之后再改回去,这样比较容易实现</p><a id="more"></a><h1><span id="code">Code</span></h1><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> maxn = <span class="number">70</span>;</span><br><span class="line"><span class="keyword">int</span> v[maxn], F, s[maxn], dep[maxn], sz[maxn];</span><br><span class="line"><span class="keyword">int</span> n, m;</span><br><span class="line"><span class="keyword">double</span> k, c[maxn], pk[maxn], f[maxn][maxn][maxn], g[maxn][maxn], ans;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">init</span><span class="params">()</span> </span>{</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i++)</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> j = <span class="number">0</span>; j <= n; j++)</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> k = <span class="number">0</span>; k <= n; k++) f[i][j][k] = <span class="number">-1e18</span>;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">dpbag</span><span class="params">(<span class="keyword">int</span> x, <span class="keyword">int</span> d)</span> </span>{</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i <= sz[x]; i++) <span class="keyword">for</span>(<span class="keyword">int</span> j = <span class="number">0</span>; j <= n; j++) g[i][j] = <span class="number">-1e18</span>;</span><br><span class="line">g[<span class="number">0</span>][<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> y = <span class="number">2</span>, i = <span class="number">0</span>; y <= n; y++) <span class="keyword">if</span>(s[y] == x) {</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> j = <span class="number">0</span>; j <= m; j++)</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> k = <span class="number">0</span>; k <= j; k++) g[i+<span class="number">1</span>][j] = max(g[i+<span class="number">1</span>][j], g[i][k] + f[y][j-k][d]);</span><br><span class="line">i++;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">dfs</span><span class="params">(<span class="keyword">int</span> x)</span> </span>{</span><br><span class="line">sz[x] = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">2</span>; i <= n; i++) <span class="keyword">if</span>(s[i] == x) sz[x]++, dep[i] = dep[x] + <span class="number">1</span>, dfs(i);</span><br><span class="line">dpbag(x, <span class="number">2</span>);</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i <= n; i++)</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> j = <span class="number">1</span>; j <= m; j++) f[x][j][i] = c[x] * k + g[sz[x]][j<span class="number">-1</span>];</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i <= n; i++) {</span><br><span class="line">dpbag(x, i + <span class="number">1</span>);</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> j = <span class="number">0</span>; j <= m; j++) f[x][j][i] = max(f[x][j][i], g[sz[x]][j] + c[x] * pk[i]);</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">freopen(<span class="string">"trans.in"</span>, <span class="string">"r"</span>, <span class="built_in">stdin</span>);</span><br><span class="line">freopen(<span class="string">"trans.out"</span>, <span class="string">"w"</span>, <span class="built_in">stdout</span>);</span><br><span class="line"><span class="keyword">int</span> x;</span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%d%d%lf"</span>, &n, &m, &k); m = min(m, n - <span class="number">1</span>);</span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%d"</span>, &F); s[<span class="number">1</span>] = F;</span><br><span class="line">pk[<span class="number">0</span>] = <span class="number">1.0</span>; <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= n + <span class="number">1</span>; i++) pk[i] = pk[i<span class="number">-1</span>] * k;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">2</span>; i <= n; i++) <span class="built_in">scanf</span>(<span class="string">"%d"</span>, &x), s[i] = x;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i++) <span class="built_in">scanf</span>(<span class="string">"%lf"</span>, &c[i]);</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = s[<span class="number">1</span>]; i != <span class="number">1</span>; i = s[i]) {</span><br><span class="line"><span class="keyword">int</span> tmp = s[i]; s[i] = <span class="number">1</span>;</span><br><span class="line">init();</span><br><span class="line">dfs(<span class="number">1</span>); ans = max(ans, f[<span class="number">1</span>][m - (tmp == <span class="number">1</span> ? <span class="number">0</span> : <span class="number">1</span>)][<span class="number">0</span>] / (<span class="number">1</span> - pk[dep[F] + <span class="number">1</span>]));</span><br><span class="line">s[i] = tmp;</span><br><span class="line">}</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%.2lf\n"</span>, ans);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>题目链接:<a href="https://www.lydsy.com/JudgeOnline/problem.php?id=1065" target="_blank" rel="noopener">BZOJ1065</a></p>
</blockquote>
<h1 id="Solution"><a href="#Solution" class="headerlink" title="Solution"></a>Solution</h1><p>有一颗有根树,根节点还有一个父亲,每个点的可靠性与儿子们相关。</p>
<p>考虑如果是一棵纯正的树,那么答案应为<br>$$<br>\sum_{i=1}^nk^{dep[i]}C_i<br>$$<br>当出现环后,也就出现了一个方程,解这个方程即可,设1号点的后继为t<br>$$<br>ans = \sum_{i=1}^nk^{dep[i]}C_i + ans\times k^{dep[x]} \\<br>ans = \frac{\sum_{i=1}^nk^{dep[i]}C_i}{1 - k^{dep[x]}}<br>$$<br>容易发现每个修改后继的点,后继一定修改成了1号节点,这样最优<br>枚举1号点的后继的深度,分母也就确定了,就可以不用考虑环了</p>
<p>于是就是在树上dp,用$f[i][j]$表示以i为根的子树中,j个点接到1的下面,最大的稳定度<br>但问题是转移时稳定度需要用深度来算啊,这样的状态无法转移<br>因此再加一维记录深度,用$f[i][j][k]$表示以i为根的子树中,i的深度为k,有j个点接到1的下面,最大的稳定度<br>这样转移时每个点u可以考虑是否接到1的下面</p>
<ol>
<li><p>如果u后继改为1,那么儿子的深度要么是1(也改了),要么是2(没改)<br>转移如下(设v是u的儿子)<br>$$<br>f[u][i][j] = C_u\times k + max(f[v][k][1], f[v][k][2])<br>$$<br>有限制条件,$\sum k = i - 1$,所以后面一项要用背包dp来做</p>
</li>
<li><p>如果u后继不改,那么儿子深度为$j + 1$或1<br>可以同理得到这样的转移<br>$$<br>f[u][i][j] = C_u \times k^j + max(f[v][k][1], f[v][k][j + 1])<br>$$<br>限制相同,用同样的背包dp做</p>
</li>
</ol>
<p>代码中背包dp部分的$g[i][j]$表示的是考虑前i个儿子,j个点后继改为1的最大值,实际上就是套路的背包dp</p>
<p>关于上面说的枚举1后继的深度,实际上可以在1的后继到1号点这条链上枚举每一个点,将他的后继改为1,dp更新答案之后再改回去,这样比较容易实现</p>
</summary>
<category term="动态规划" scheme="http://rentingxutx.github.io/categories/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"/>
<category term="树形dp" scheme="http://rentingxutx.github.io/categories/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/%E6%A0%91%E5%BD%A2dp/"/>
<category term="背包dp" scheme="http://rentingxutx.github.io/categories/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/%E8%83%8C%E5%8C%85dp/"/>
</entry>
<entry>
<title>BZOJ1064 [Noi2008]假面舞会</title>
<link href="http://rentingxutx.github.io/2019/08/31/BZOJ1064-Noi2008-%E5%81%87%E9%9D%A2%E8%88%9E%E4%BC%9A/"/>
<id>http://rentingxutx.github.io/2019/08/31/BZOJ1064-Noi2008-假面舞会/</id>
<published>2019-08-31T06:58:15.000Z</published>
<updated>2019-08-31T09:08:31.128Z</updated>
<content type="html"><![CDATA[<blockquote><p>题目链接:<a href="https://www.lydsy.com/JudgeOnline/problem.php?id=1064" target="_blank" rel="noopener">BZOJ1064</a></p></blockquote><h1><span id="solution">Solution</span></h1><p>对于每条信息连一条边权为1的正向边和一条边权为-1的反向边,然后染色问题,dfs一遍为每个点分配编号。若出现矛盾直接退出,否则得到这个连通块的最大可能数。<br>对每个联通的结果求gcd,就得到了整体的最大可能数<br>最小值就是最大值的第一个大于等于3的因数<br>注意小于3为无解<br>正确性显然,关键是正向反向的边权和染色<br><a id="more"></a></p><h1><span id="code">Code</span></h1><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> maxn = <span class="number">100010</span>, maxm = <span class="number">2000010</span>;</span><br><span class="line"><span class="keyword">int</span> n, m, head[maxn], ver[maxm], edge[maxm], Next[maxm], tot = <span class="number">1</span>;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">add</span><span class="params">(<span class="keyword">int</span> x, <span class="keyword">int</span> y, <span class="keyword">int</span> z)</span> </span>{ ver[++tot] = y, edge[tot] = z, Next[tot] = head[x], head[x] = tot; }</span><br><span class="line"><span class="keyword">int</span> col[maxn], cir[maxm], cnt, v[maxn], cmx = <span class="number">1</span>, cmn = <span class="number">1</span>, len = <span class="number">0</span>;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">dfs</span><span class="params">(<span class="keyword">int</span> x, <span class="keyword">int</span> cur, <span class="keyword">int</span> pa)</span> </span>{</span><br><span class="line">col[x] = cur; v[x] = <span class="number">1</span>;</span><br><span class="line">cmn = min(cmn, col[x]), cmx = max(cmx, col[x]);</span><br><span class="line"><span class="comment">// cout << x << ' ' << cur << endl;</span></span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = head[x]; i; i = Next[i]) <span class="keyword">if</span>(i != pa) {</span><br><span class="line"><span class="keyword">if</span>(v[ver[i]]) cir[++cnt] = <span class="built_in">abs</span>(col[ver[i]] - (cur + edge[i]));</span><br><span class="line"><span class="keyword">else</span> dfs(ver[i], cur + edge[i], i ^ <span class="number">1</span>);</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">gcd</span><span class="params">(<span class="keyword">int</span> x, <span class="keyword">int</span> y)</span> </span>{ <span class="keyword">return</span> y ? gcd(y, x % y) : x; }</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"><span class="comment">// freopen("2.in", "r", stdin);</span></span><br><span class="line"><span class="keyword">int</span> x, y;</span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%d%d"</span>, &n, &m);</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= m; i++) <span class="built_in">scanf</span>(<span class="string">"%d%d"</span>, &x, &y), add(x, y, <span class="number">1</span>), add(y, x, <span class="number">-1</span>);</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i++) <span class="keyword">if</span>(!v[i]) dfs(i, <span class="number">1</span>, <span class="number">0</span>), len += cmx - cmn + <span class="number">1</span>, cmx = cmn = <span class="number">1</span>;</span><br><span class="line"><span class="comment">// for(int i = 1; i <= cnt; i++) cout << cir[i] << ' '; cout << endl;</span></span><br><span class="line"><span class="keyword">int</span> mx = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= cnt; i++) mx = gcd(mx, cir[i]);</span><br><span class="line"><span class="keyword">if</span>(mx == <span class="number">1</span> || mx == <span class="number">2</span>) { <span class="built_in">printf</span>(<span class="string">"-1 -1\n"</span>); <span class="keyword">return</span> <span class="number">0</span>; }</span><br><span class="line"><span class="keyword">if</span>(mx == <span class="number">0</span>){ </span><br><span class="line"><span class="keyword">if</span>(len < <span class="number">3</span>) <span class="built_in">printf</span>(<span class="string">"-1 -1\n"</span>);</span><br><span class="line"><span class="keyword">else</span> <span class="built_in">printf</span>(<span class="string">"%d %d\n"</span>, len, <span class="number">3</span>); </span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>; </span><br><span class="line">}</span><br><span class="line"><span class="keyword">int</span> mn;</span><br><span class="line"><span class="keyword">for</span>(mn = <span class="number">3</span>; mn <= mx; mn++) <span class="keyword">if</span>(mx % mn == <span class="number">0</span>) <span class="keyword">break</span>;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%d %d\n"</span>, mx, mn); </span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>题目链接:<a href="https://www.lydsy.com/JudgeOnline/problem.php?id=1064" target="_blank" rel="noopener">BZOJ1064</a></p>
</blockquote>
<h1 id="Solution"><a href="#Solution" class="headerlink" title="Solution"></a>Solution</h1><p>对于每条信息连一条边权为1的正向边和一条边权为-1的反向边,然后染色问题,dfs一遍为每个点分配编号。若出现矛盾直接退出,否则得到这个连通块的最大可能数。<br>对每个联通的结果求gcd,就得到了整体的最大可能数<br>最小值就是最大值的第一个大于等于3的因数<br>注意小于3为无解<br>正确性显然,关键是正向反向的边权和染色<br>
</summary>
<category term="图论" scheme="http://rentingxutx.github.io/categories/%E5%9B%BE%E8%AE%BA/"/>
<category term="染色" scheme="http://rentingxutx.github.io/categories/%E5%9B%BE%E8%AE%BA/%E6%9F%93%E8%89%B2/"/>
<category term="染色" scheme="http://rentingxutx.github.io/tags/%E6%9F%93%E8%89%B2/"/>
</entry>
<entry>
<title>BZOJ1063 [Noi2008]道路设计</title>
<link href="http://rentingxutx.github.io/2019/08/31/BZOJ1063-Noi2008-%E9%81%93%E8%B7%AF%E8%AE%BE%E8%AE%A1/"/>
<id>http://rentingxutx.github.io/2019/08/31/BZOJ1063-Noi2008-道路设计/</id>
<published>2019-08-31T06:03:06.000Z</published>
<updated>2019-08-31T06:55:49.160Z</updated>
<content type="html"><![CDATA[<blockquote><p>题目链接:<a href="https://www.lydsy.com/JudgeOnline/problem.php?id=1063" target="_blank" rel="noopener">BZOJ1063</a></p></blockquote><h1><span id="solution">Solution</span></h1><p>题意:给定一棵有根树,可以将任意条路径上的边打上标记,每个点的“不便利度”为其到根的路径上未标记边的条数,要求最小化所有点不便利度的最大值,并求方案数</p><p>树形dp,用$ f[i][0/1] $表示以i为根的子树中,i连向父亲的边是否标记,最小的不便利度<br>考试时,我直接二分f值,实际上也可以递推,方法类似下面要说的g</p><p>这里有一个非常有用的性质<br>考虑树链剖分时的轻重链的划分,可以发现不便利度也就是轻边个数,因此有方法使不便利度最大不会超过$ log_2n $,这样就可以进一步设计状态了</p><p>用$ g[i][0/1/2][j] $表示以i为根的子树中,i连向子树的边标记了0或1或2条,不便利度为j的方案数<br>然后背包dp转移即可<br>至于为什么这里开了3个,f只开了2个,因为算f时由贪心策略,每个点连出去的边一定有一条标记,但算方案数时就不一定了</p><a id="more"></a><h1><span id="code">Code</span></h1><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> maxn = <span class="number">100010</span>;</span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">long</span> <span class="keyword">long</span> ll;</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">read</span><span class="params">()</span> </span>{</span><br><span class="line"><span class="keyword">int</span> res = <span class="number">0</span>, f = <span class="number">1</span>; <span class="keyword">char</span> c = getchar();</span><br><span class="line"><span class="keyword">while</span>(c != <span class="string">'-'</span> && (c < <span class="string">'0'</span> || c > <span class="string">'9'</span>)) c = getchar(); <span class="keyword">if</span>(c == <span class="string">'-'</span>) f = <span class="number">-1</span>, c = getchar();</span><br><span class="line"><span class="keyword">while</span>(c >= <span class="string">'0'</span> && c <= <span class="string">'9'</span>) res = (res << <span class="number">3</span>) + (res << <span class="number">1</span>) + c - <span class="string">'0'</span>, c = getchar(); <span class="keyword">return</span> f * res;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">int</span> n, m, mod, head[maxn], ver[maxn * <span class="number">2</span>], Next[maxn * <span class="number">2</span>], tot, N;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">add</span><span class="params">(<span class="keyword">int</span> x, <span class="keyword">int</span> y)</span> </span>{ ver[++tot] = y, Next[tot] = head[x], head[x] = tot; }</span><br><span class="line"><span class="keyword">int</span> f[maxn][<span class="number">2</span>], q[maxn];</span><br><span class="line">ll g[maxn][<span class="number">3</span>][<span class="number">21</span>];</span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">valid</span><span class="params">(<span class="keyword">int</span> x, <span class="keyword">int</span> pa, <span class="keyword">int</span> mid, <span class="keyword">int</span> lim)</span> </span>{</span><br><span class="line"><span class="keyword">int</span> cnt = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = head[x]; i; i = Next[i]) <span class="keyword">if</span>(ver[i] != pa) {</span><br><span class="line"><span class="keyword">if</span>(f[ver[i]][<span class="number">0</span>] + <span class="number">1</span> <= mid) <span class="keyword">continue</span>;</span><br><span class="line"><span class="keyword">if</span>(f[ver[i]][<span class="number">1</span>] > mid) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">cnt++;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span>(cnt > lim) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"><span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">dp</span><span class="params">(<span class="keyword">int</span> x, <span class="keyword">int</span> pa)</span> </span>{</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = head[x]; i; i = Next[i]) <span class="keyword">if</span>(ver[i] != pa) dp(ver[i], x);</span><br><span class="line"><span class="keyword">int</span> l = <span class="number">0</span>, r = n - <span class="number">1</span>;</span><br><span class="line"><span class="keyword">while</span>(l < r) {</span><br><span class="line"><span class="keyword">int</span> mid = (l + r) >> <span class="number">1</span>;</span><br><span class="line"><span class="keyword">if</span>(valid(x, pa, mid, <span class="number">1</span>)) r = mid; <span class="keyword">else</span> l = mid + <span class="number">1</span>; </span><br><span class="line">}</span><br><span class="line">f[x][<span class="number">1</span>] = l;</span><br><span class="line">l = <span class="number">0</span>, r = n - <span class="number">1</span>;</span><br><span class="line"><span class="keyword">while</span>(l < r) {</span><br><span class="line"><span class="keyword">int</span> mid = (l + r) >> <span class="number">1</span>;</span><br><span class="line"><span class="keyword">if</span>(valid(x, pa, mid, <span class="number">2</span>)) r = mid; <span class="keyword">else</span> l = mid + <span class="number">1</span>;</span><br><span class="line">}</span><br><span class="line">f[x][<span class="number">0</span>] = l;</span><br><span class="line"><span class="comment">//cout << x << ' ' << f[x][0] << ' ' << f[x][1] << endl;</span></span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">dfs</span><span class="params">(<span class="keyword">int</span> x, <span class="keyword">int</span> pa)</span> </span>{</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = head[x]; i; i = Next[i]) <span class="keyword">if</span>(ver[i] != pa) dfs(ver[i], x);</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> j = <span class="number">0</span>; j <= N; j++) {</span><br><span class="line">g[x][<span class="number">0</span>][j] = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = head[x]; i; i = Next[i]) <span class="keyword">if</span>(ver[i] != pa) {</span><br><span class="line"><span class="keyword">int</span> y = ver[i];</span><br><span class="line">g[x][<span class="number">2</span>][j] = (g[x][<span class="number">1</span>][j] * g[y][<span class="number">1</span>][j] % mod + (j ? g[x][<span class="number">2</span>][j] * g[y][<span class="number">2</span>][j<span class="number">-1</span>] % mod : <span class="number">0</span>)) % mod;</span><br><span class="line">g[x][<span class="number">1</span>][j] = (g[x][<span class="number">0</span>][j] * g[y][<span class="number">1</span>][j] % mod + (j ? g[x][<span class="number">1</span>][j] * g[y][<span class="number">2</span>][j<span class="number">-1</span>] % mod : <span class="number">0</span>)) % mod;</span><br><span class="line"><span class="keyword">if</span>(j) (g[x][<span class="number">0</span>][j] *= g[y][<span class="number">2</span>][j<span class="number">-1</span>] % mod) %= mod;</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span>(Next[head[x]]) g[x][<span class="number">0</span>][j] = <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line">(g[x][<span class="number">1</span>][j] += g[x][<span class="number">0</span>][j]) %= mod;</span><br><span class="line">(g[x][<span class="number">2</span>][j] += g[x][<span class="number">1</span>][j]) %= mod;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//cout << x << endl;</span></span><br><span class="line"><span class="comment">//for(int i = 0; i <= N; i++) cout << g[x][2][i] << ' '; cout << endl;</span></span><br><span class="line"><span class="comment">//for(int i = 0; i <= N; i++) cout << g[x][1][i] << ' '; cout << endl;</span></span><br><span class="line"><span class="comment">//for(int i = 0; i <= N; i++) cout << g[x][0][i] << ' '; cout << endl;</span></span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"><span class="comment">//freopen("design.in", "r", stdin);</span></span><br><span class="line"><span class="comment">//freopen("design.out", "w", stdout);</span></span><br><span class="line"><span class="keyword">int</span> x, y;</span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%d%d%d"</span>, &n, &m, &mod);</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= m; i++) <span class="built_in">scanf</span>(<span class="string">"%d%d"</span>, &x, &y), add(x, y), add(y, x);</span><br><span class="line"><span class="keyword">if</span>(m < n - <span class="number">1</span>) { <span class="built_in">printf</span>(<span class="string">"-1\n-1\n"</span>); <span class="keyword">return</span> <span class="number">0</span>; }</span><br><span class="line">dp(<span class="number">1</span>, <span class="number">0</span>); N = f[<span class="number">1</span>][<span class="number">0</span>];</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%d\n"</span>, f[<span class="number">1</span>][<span class="number">0</span>]);</span><br><span class="line">dfs(<span class="number">1</span>, <span class="number">0</span>);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%lld\n"</span>, (g[<span class="number">1</span>][<span class="number">2</span>][N]) % mod);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>题目链接:<a href="https://www.lydsy.com/JudgeOnline/problem.php?id=1063" target="_blank" rel="noopener">BZOJ1063</a></p>
</blockquote>
<h1 id="Solution"><a href="#Solution" class="headerlink" title="Solution"></a>Solution</h1><p>题意:给定一棵有根树,可以将任意条路径上的边打上标记,每个点的“不便利度”为其到根的路径上未标记边的条数,要求最小化所有点不便利度的最大值,并求方案数</p>
<p>树形dp,用$ f[i][0/1] $表示以i为根的子树中,i连向父亲的边是否标记,最小的不便利度<br>考试时,我直接二分f值,实际上也可以递推,方法类似下面要说的g</p>
<p>这里有一个非常有用的性质<br>考虑树链剖分时的轻重链的划分,可以发现不便利度也就是轻边个数,因此有方法使不便利度最大不会超过$ log_2n $,这样就可以进一步设计状态了</p>
<p>用$ g[i][0/1/2][j] $表示以i为根的子树中,i连向子树的边标记了0或1或2条,不便利度为j的方案数<br>然后背包dp转移即可<br>至于为什么这里开了3个,f只开了2个,因为算f时由贪心策略,每个点连出去的边一定有一条标记,但算方案数时就不一定了</p>
</summary>
<category term="动态规划" scheme="http://rentingxutx.github.io/categories/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"/>
<category term="树形dp" scheme="http://rentingxutx.github.io/categories/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/%E6%A0%91%E5%BD%A2dp/"/>
<category term="树形dp" scheme="http://rentingxutx.github.io/tags/%E6%A0%91%E5%BD%A2dp/"/>
</entry>
<entry>
<title>BZOJ1062 [NOI2008]糖果雨</title>
<link href="http://rentingxutx.github.io/2019/08/31/BZOJ1062-NOI2008-%E7%B3%96%E6%9E%9C%E9%9B%A8/"/>
<id>http://rentingxutx.github.io/2019/08/31/BZOJ1062-NOI2008-糖果雨/</id>
<published>2019-08-31T03:53:56.000Z</published>
<updated>2019-09-09T10:35:26.768Z</updated>
<content type="html"><![CDATA[<blockquote><p>题目链接:<a href="https://www.lydsy.com/JudgeOnline/problem.php?id=1062" target="_blank" rel="noopener">BZOJ1062</a></p></blockquote><h1><span id="solution">Solution</span></h1><p>题意:一些线段在左右飘,支持加入和删除线段,多次询问某个时刻某个区间接触的线段个数</p><p>我们把一条线段看成是平面坐标系中横坐标为左端点,纵坐标为线段长度的一个点.同时将横坐标复制一边,代表反方向,这样就变成了标准的循环.</p><p>考虑每个询问可以覆盖的范围,那么也就是求这个范围内的点数</p><p>如果时刻都为0的话,那么每个询问的范围是一个矩形切掉一个角形成的五边形,可以补成一个平行四边形.由于翻倍了,因此是两个</p><p>当时刻不为0时,这两个平行四边形会左右平移,要考虑到有重合部分的边界情况.</p><p>平行四边形不方便处理,可以坐标变换,新的纵坐标为原来的横纵坐标之和或差,对于两个平行四边形有不同的变换方式,要建两个坐标系</p><p>懒得画图了,贴一个<a href="https://oi.men.ci/noi2008-candy/" target="_blank" rel="noopener">写的很清楚的链接</a><br><a id="more"></a></p><h1><span id="code">Code</span></h1><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> maxn = <span class="number">200010</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">int</span> n, len, rev[<span class="number">1000010</span>], X[maxn], Y[maxn], cnt;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">BIT</span>{</span></span><br><span class="line"><span class="keyword">int</span> c[<span class="number">2020</span>][<span class="number">4020</span>];</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">add</span><span class="params">(<span class="keyword">int</span> x, <span class="keyword">int</span> y, <span class="keyword">int</span> z)</span> </span>{</span><br><span class="line"><span class="comment">//cout << x << ' ' << y << endl;</span></span><br><span class="line">x++; y++;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = x; i <= <span class="number">2010</span>; i += i&-i)</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> j = y; j <= <span class="number">4010</span>; j += j&-j)</span><br><span class="line">c[i][j] += z;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">query</span><span class="params">(<span class="keyword">int</span> x, <span class="keyword">int</span> y)</span> </span>{</span><br><span class="line"><span class="keyword">if</span>(x < <span class="number">0</span> || y < <span class="number">0</span>) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">x++; y++;</span><br><span class="line"><span class="keyword">if</span>(x > <span class="number">2</span> * len) x = <span class="number">2</span> * len + <span class="number">1</span>;</span><br><span class="line"><span class="keyword">if</span>(y > <span class="number">4</span> * len) y = <span class="number">4</span> * len + <span class="number">1</span>;</span><br><span class="line"><span class="keyword">int</span> res = <span class="number">0</span>;</span><br><span class="line"><span class="comment">//cout << x << ' ' << y << ' ';</span></span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = x; i; i -= i&-i)</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> j = y; j; j -= j&-j) res += c[i][j];</span><br><span class="line"><span class="comment">//cout << res << endl;</span></span><br><span class="line"><span class="keyword">return</span> res;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">sum</span><span class="params">(<span class="keyword">int</span> x1, <span class="keyword">int</span> y1, <span class="keyword">int</span> x2, <span class="keyword">int</span> y2)</span> </span>{</span><br><span class="line"><span class="comment">//cout << x1 << ' ' << y1 << ' ' << x2 << ' ' << y2 << endl;</span></span><br><span class="line"><span class="keyword">return</span> query(x2, y2) - query(x1<span class="number">-1</span>, y2) - query(x2, y1<span class="number">-1</span>) + query(x1<span class="number">-1</span>, y1<span class="number">-1</span>);</span><br><span class="line">}</span><br><span class="line">}bit1, bit2;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">freopen(<span class="string">"candy.in"</span>, <span class="string">"r"</span>, <span class="built_in">stdin</span>);</span><br><span class="line">freopen(<span class="string">"candy.out"</span>, <span class="string">"w"</span>, <span class="built_in">stdout</span>);</span><br><span class="line"><span class="keyword">int</span> o, t, l, r, c, d;</span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%d%d"</span>, &n, &len);</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i++) {</span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%d"</span>, &o);</span><br><span class="line"><span class="keyword">if</span>(o == <span class="number">1</span>) {</span><br><span class="line">cnt++;</span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%d%d%d%d%d"</span>, &t, &c, &l, &r, &d);</span><br><span class="line">rev[c] = cnt; </span><br><span class="line">t = (t - d * l + len * <span class="number">2</span>) % (len * <span class="number">2</span>); r = r - l;</span><br><span class="line">bit1.add(t, r + t, <span class="number">1</span>); bit2.add(t, r - t + <span class="number">2</span> * len, <span class="number">1</span>);</span><br><span class="line">X[cnt] = t; Y[cnt] = r;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span>(o == <span class="number">2</span>) {</span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%d%d%d"</span>, &t, &l, &r);</span><br><span class="line"><span class="keyword">int</span> d = (r == len);</span><br><span class="line">t %= (len * <span class="number">2</span>);</span><br><span class="line"><span class="keyword">int</span> res = bit1.sum(t, t + l, t + r, <span class="number">4</span> * len) + bit1.sum(<span class="number">0</span>, t + l - <span class="number">2</span> * len, t + r - <span class="number">2</span> * len - d, <span class="number">4</span> * len);</span><br><span class="line"><span class="comment">//cout << res << endl;</span></span><br><span class="line">res += bit2.sum(t - r, l - t + <span class="number">2</span> * len, t - <span class="number">1</span>, <span class="number">4</span> * len) + bit2.sum(t - r + <span class="number">2</span> * len + d, l - t, <span class="number">2</span> * len, <span class="number">4</span> * len);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%d\n"</span>, res);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span>(o == <span class="number">3</span>) {</span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%d%d"</span>, &t, &l);</span><br><span class="line"><span class="keyword">int</span> i = rev[l];</span><br><span class="line">bit1.add(X[i], Y[i] + X[i], <span class="number">-1</span>); </span><br><span class="line">bit2.add(X[i], Y[i] - X[i] + <span class="number">2</span> * len, <span class="number">-1</span>);</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>题目链接:<a href="https://www.lydsy.com/JudgeOnline/problem.php?id=1062" target="_blank" rel="noopener">BZOJ1062</a></p>
</blockquote>
<h1 id="Solution"><a href="#Solution" class="headerlink" title="Solution"></a>Solution</h1><p>题意:一些线段在左右飘,支持加入和删除线段,多次询问某个时刻某个区间接触的线段个数</p>
<p>我们把一条线段看成是平面坐标系中横坐标为左端点,纵坐标为线段长度的一个点.同时将横坐标复制一边,代表反方向,这样就变成了标准的循环.</p>
<p>考虑每个询问可以覆盖的范围,那么也就是求这个范围内的点数</p>
<p>如果时刻都为0的话,那么每个询问的范围是一个矩形切掉一个角形成的五边形,可以补成一个平行四边形.由于翻倍了,因此是两个</p>
<p>当时刻不为0时,这两个平行四边形会左右平移,要考虑到有重合部分的边界情况.</p>
<p>平行四边形不方便处理,可以坐标变换,新的纵坐标为原来的横纵坐标之和或差,对于两个平行四边形有不同的变换方式,要建两个坐标系</p>
<p>懒得画图了,贴一个<a href="https://oi.men.ci/noi2008-candy/" target="_blank" rel="noopener">写的很清楚的链接</a><br>
</summary>
<category term="数据结构" scheme="http://rentingxutx.github.io/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/"/>
<category term="树状数组" scheme="http://rentingxutx.github.io/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E6%A0%91%E7%8A%B6%E6%95%B0%E7%BB%84/"/>
<category term="树状数组" scheme="http://rentingxutx.github.io/tags/%E6%A0%91%E7%8A%B6%E6%95%B0%E7%BB%84/"/>
<category term="数形转换" scheme="http://rentingxutx.github.io/tags/%E6%95%B0%E5%BD%A2%E8%BD%AC%E6%8D%A2/"/>
</entry>
<entry>
<title>BZOJ1921 [Ctsc2010]珠宝商</title>
<link href="http://rentingxutx.github.io/2019/08/28/BZOJ1921-Ctsc2010-%E7%8F%A0%E5%AE%9D%E5%95%86/"/>
<id>http://rentingxutx.github.io/2019/08/28/BZOJ1921-Ctsc2010-珠宝商/</id>
<published>2019-08-28T13:09:46.000Z</published>
<updated>2019-08-30T12:25:12.096Z</updated>
<content type="html"><![CDATA[<blockquote><p>题目链接<a href="https://www.lydsy.com/JudgeOnline/problem.php?id=1921" target="_blank" rel="noopener">BZOJ1921</a></p></blockquote><h1><span id="solution">Solution</span></h1><p>本题综合利用后缀自动机的呀</p><p>首先第一想法是怎么写暴力,从每个点为起点开始想下走,同时在SAM上走DAG,对于树上的每走到一个点,把答案加上其SAM对应节点的endpos大小即可,复杂度$ O(n^2) $</p><p>然后看到这种统计路径的题,回想到点分和DSU,先试试点分,那么需要做的是统计以经过某个点x的路径的贡献,假设这条路径是$ u \to x \to v $<br>点分的一般思路,如果可以单独算出$ u \to x $和$ x \to v $,那么拼在一起就好了<br>但是如何拼在一起呢<br>考虑可以在M串上统计,每个u到x在匹配的位置上加一个标记,那么最后的答案就是每个位置 向前匹配的标记和*向后匹配的标记和 之和<br>发现上面说的两种路径其中一种可以变成反串上的另一种,因此只需考虑一种就可以</p><p>然后就要求后缀自动机理解透彻了<br>网上的题解都说要在后缀树上统计$ u \to x $的路径贡献<br>然后我就想,为什么还要搞后缀树,直接在DAG上统计$ x \to v $的贡献不就可以啦,每次在后面加一个字符,方法同第一种暴力一样啊<br>然而在网上找不到这种做法,于是我就手画了好多后缀自动机和后缀树(因为画挂了很多次),然后找到了原因<br>如果我们走DAG来计算贡献,那么走到一个点时,要计入的是以$ x…v $为前缀的串的个数,但在DAG上走到一个位置,这个节点代表的字串可以有在$x…v$前面加了几个字符,然后就不满足要求了,但我们同样会计入答案。也就是说这样会统计多<br>但如果在后缀树上统计$ u \to x $,那么每次是在前面加一个字符,需要向儿子走,但可以保证之后走到的每一个点代表的所有串都是以$ u…x $为后缀的,就是当且仅当的意思,所以可以正确统计<br>这个还是要搞张图看看啊</p><p>后缀自动机一个重要性质:自动机的par树即是其反串的后缀树(所以par树也叫前缀树,前缀树是反串的后缀树,多好)<br>然后建后缀树时记录一下每个点到其儿子走的是哪个字符的转移边</p><p>这样点分的做法就完善了,由于在每个分治点都要在后缀树上将标记推到儿子并统计,复杂度是$ O(nlogn + nm) $,后面一部分成了瓶颈</p><p>前人想出了一个很神仙的操作,我们发现第一个解法(暴力)复杂度与m无关,因此可以考虑当分治的子树大小低于某个值时直接暴力统计即可<br>当这个值设为$ \sqrt n $时最优,复杂度可以控制在$ O((n + m)\sqrt n) $</p><p>写在最后<br>后缀系列算法真的好神啊,后缀自动机和后缀树的坑好深啊<br>主要是有各种各样的性质,互相转换又即为方便<br>然后通过这道题我还练熟了手画自动机[滑稽]</p><a id="more"></a><h1><span id="code">Code</span></h1><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> rep(i, a, b) for(int i(a), i##_ORZ(b); i <= i##_ORZ; i++)</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> drep(i, a, b) for(int i(a), i##_ORZ(b); i >= i##_ORZ; i--)</span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> maxn = <span class="number">200010</span>;</span><br><span class="line"><span class="keyword">char</span> tr[maxn], eg[maxn];</span><br><span class="line"><span class="keyword">int</span> n, m, head[maxn], ver[maxn], Next[maxn], tot, totsz, mn, rt, sz[maxn], v[maxn], B;</span><br><span class="line"><span class="keyword">long</span> <span class="keyword">long</span> ans;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">add</span><span class="params">(<span class="keyword">int</span> x, <span class="keyword">int</span> y)</span> </span>{ ver[++tot] = y, Next[tot] = head[x], head[x] = tot; }</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">SAM</span>{</span></span><br><span class="line"><span class="keyword">int</span> par[maxn], ch[maxn][<span class="number">30</span>], val[maxn], cnt, las, rt;</span><br><span class="line"><span class="keyword">int</span> sz[maxn], pos[maxn], nxt[maxn][<span class="number">30</span>], c[maxn], A[maxn], nod[maxn], sum[maxn];</span><br><span class="line"><span class="keyword">char</span> S[maxn];</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">extend</span><span class="params">(<span class="keyword">int</span> x, <span class="keyword">int</span> po)</span> </span>{</span><br><span class="line"><span class="keyword">int</span> p = las, np = ++cnt; nod[po] = np;</span><br><span class="line">val[np] = val[p] + <span class="number">1</span>; sz[np] = <span class="number">1</span>; pos[np] = po;</span><br><span class="line"><span class="keyword">while</span>(p && !ch[p][x]) ch[p][x] = np, p = par[p];</span><br><span class="line"><span class="keyword">if</span>(!p) par[np] = rt;</span><br><span class="line"><span class="keyword">else</span> {</span><br><span class="line"><span class="keyword">int</span> q = ch[p][x];</span><br><span class="line"><span class="keyword">if</span>(val[q] == val[p] + <span class="number">1</span>) par[np] = q;</span><br><span class="line"><span class="keyword">else</span> {</span><br><span class="line"><span class="keyword">int</span> nq = ++cnt; val[nq] = val[p] + <span class="number">1</span>;</span><br><span class="line"><span class="built_in">memcpy</span>(ch[nq], ch[q], <span class="keyword">sizeof</span>(ch[nq]));</span><br><span class="line">par[nq] = par[q];</span><br><span class="line">par[q] = par[np] = nq;</span><br><span class="line"><span class="keyword">while</span>(p && ch[p][x] == q) ch[p][x] = nq, p = par[p];</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">las = np;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">build</span><span class="params">()</span> </span>{</span><br><span class="line">cnt = rt = las = <span class="number">1</span>;</span><br><span class="line">rep(i, <span class="number">1</span>, m) extend(S[i] - <span class="string">'a'</span>, i);</span><br><span class="line">rep(i, <span class="number">1</span>, cnt) c[val[i]]++;</span><br><span class="line">rep(i, <span class="number">1</span>, m) c[i] += c[i<span class="number">-1</span>];</span><br><span class="line">rep(i, <span class="number">1</span>, cnt) A[c[val[i]]--] = i;</span><br><span class="line">drep(i, cnt, <span class="number">1</span>) {</span><br><span class="line"><span class="keyword">int</span> x = A[i];</span><br><span class="line">sz[par[x]] += sz[x]; </span><br><span class="line"><span class="keyword">if</span>(!pos[par[x]]) pos[par[x]] = pos[x];</span><br><span class="line">nxt[par[x]][S[pos[x] - val[par[x]]] - <span class="string">'a'</span>] = x;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">clear</span><span class="params">()</span> </span>{</span><br><span class="line">rep(i, <span class="number">1</span>, cnt) sum[i] = <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">pushdown</span><span class="params">()</span> </span>{</span><br><span class="line">rep(i, <span class="number">1</span>, cnt) sum[A[i]] += sum[par[A[i]]];</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">match</span><span class="params">(<span class="keyword">int</span> x, <span class="keyword">int</span> pa, <span class="keyword">int</span> cur, <span class="keyword">int</span> len)</span> </span>{</span><br><span class="line"><span class="keyword">if</span>(len == val[cur]) cur = nxt[cur][tr[x] - <span class="string">'a'</span>];</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span>(S[pos[cur] - len] != tr[x]) cur = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">if</span>(!cur) <span class="keyword">return</span>;</span><br><span class="line">len++; sum[cur]++;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = head[x]; i; i = Next[i]) <span class="keyword">if</span>(ver[i] != pa && !v[ver[i]])</span><br><span class="line">match(ver[i], x, cur, len);</span><br><span class="line">}</span><br><span class="line">}sam[<span class="number">2</span>];</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">getroot</span><span class="params">(<span class="keyword">int</span> x, <span class="keyword">int</span> pa)</span> </span>{</span><br><span class="line">sz[x] = <span class="number">1</span>; <span class="keyword">int</span> res = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = head[x]; i; i = Next[i]) <span class="keyword">if</span>(ver[i] != pa && !v[ver[i]]) {</span><br><span class="line">getroot(ver[i], x);</span><br><span class="line">sz[x] += sz[ver[i]];</span><br><span class="line">res = max(res, sz[ver[i]]);</span><br><span class="line">}</span><br><span class="line">res = max(res, totsz - sz[x]);</span><br><span class="line"><span class="keyword">if</span>(res < mn) rt = x, mn = res;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">calc</span><span class="params">(<span class="keyword">int</span> x, <span class="keyword">int</span> pa, <span class="keyword">int</span> cur)</span> </span>{</span><br><span class="line">cur = sam[<span class="number">0</span>].ch[cur][tr[x] - <span class="string">'a'</span>];</span><br><span class="line"><span class="keyword">if</span>(!cur) <span class="keyword">return</span>;</span><br><span class="line">ans += sam[<span class="number">0</span>].sz[cur];</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = head[x]; i; i = Next[i]) <span class="keyword">if</span>(ver[i] != pa && !v[ver[i]])</span><br><span class="line">calc(ver[i], x, cur);</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">brute</span><span class="params">(<span class="keyword">int</span> x, <span class="keyword">int</span> pa)</span> </span>{</span><br><span class="line">calc(x, <span class="number">0</span>, <span class="number">1</span>);</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = head[x]; i; i = Next[i]) <span class="keyword">if</span>(ver[i] != pa && !v[ver[i]])</span><br><span class="line">brute(ver[i], x);</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Insert</span><span class="params">(<span class="keyword">int</span> x)</span> </span>{</span><br><span class="line">sam[<span class="number">0</span>].clear(); sam[<span class="number">1</span>].clear();</span><br><span class="line">sam[<span class="number">0</span>].match(x, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>); sam[<span class="number">0</span>].pushdown();</span><br><span class="line">sam[<span class="number">1</span>].match(x, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>); sam[<span class="number">1</span>].pushdown();</span><br><span class="line">rep(i, <span class="number">1</span>, m) ans += <span class="number">1l</span>l * sam[<span class="number">0</span>].sum[sam[<span class="number">0</span>].nod[i]] * sam[<span class="number">1</span>].sum[sam[<span class="number">1</span>].nod[m - i + <span class="number">1</span>]];</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Delete</span><span class="params">(<span class="keyword">int</span> x, <span class="keyword">int</span> pa)</span> </span>{</span><br><span class="line">sam[<span class="number">0</span>].clear(); sam[<span class="number">1</span>].clear();</span><br><span class="line">sam[<span class="number">0</span>].match(x, <span class="number">0</span>, sam[<span class="number">0</span>].ch[<span class="number">1</span>][tr[pa] - <span class="string">'a'</span>], <span class="number">1</span>); sam[<span class="number">0</span>].pushdown();</span><br><span class="line">sam[<span class="number">1</span>].match(x, <span class="number">0</span>, sam[<span class="number">1</span>].ch[<span class="number">1</span>][tr[pa] - <span class="string">'a'</span>], <span class="number">1</span>); sam[<span class="number">1</span>].pushdown();</span><br><span class="line">rep(i, <span class="number">1</span>, m) ans -= <span class="number">1l</span>l * sam[<span class="number">0</span>].sum[sam[<span class="number">0</span>].nod[i]] * sam[<span class="number">1</span>].sum[sam[<span class="number">1</span>].nod[m - i + <span class="number">1</span>]];</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">getsz</span><span class="params">(<span class="keyword">int</span> x, <span class="keyword">int</span> pa)</span> </span>{</span><br><span class="line">sz[x] = <span class="number">1</span>; </span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = head[x]; i; i = Next[i]) <span class="keyword">if</span>(!v[ver[i]] && ver[i] != pa) </span><br><span class="line">getsz(ver[i], x), sz[x] += sz[ver[i]];</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">DFS</span><span class="params">(<span class="keyword">int</span> x)</span> </span>{</span><br><span class="line"><span class="keyword">if</span>(totsz <= B) { brute(x, <span class="number">0</span>); <span class="keyword">return</span>; }</span><br><span class="line">Insert(x); v[x] = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = head[x]; i; i = Next[i]) <span class="keyword">if</span>(!v[ver[i]]) Delete(ver[i], x);</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = head[x]; i; i = Next[i]) <span class="keyword">if</span>(!v[ver[i]]) {</span><br><span class="line">getsz(ver[i], x);</span><br><span class="line">totsz = mn = sz[ver[i]];</span><br><span class="line">getroot(ver[i], x);</span><br><span class="line">DFS(rt);</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"><span class="keyword">int</span> x, y;</span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%d%d"</span>, &n, &m);</span><br><span class="line">rep(i, <span class="number">1</span>, n - <span class="number">1</span>) <span class="built_in">scanf</span>(<span class="string">"%d%d"</span>, &x, &y), add(x, y), add(y, x);</span><br><span class="line">B = <span class="built_in">ceil</span>(<span class="built_in">sqrt</span>(n));</span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%s%s"</span>, tr + <span class="number">1</span>, eg + <span class="number">1</span>);</span><br><span class="line">m = <span class="built_in">strlen</span>(eg + <span class="number">1</span>);</span><br><span class="line">rep(i, <span class="number">1</span>, m) sam[<span class="number">0</span>].S[i] = eg[i], sam[<span class="number">1</span>].S[m - i + <span class="number">1</span>] = eg[i];</span><br><span class="line">sam[<span class="number">0</span>].build(); sam[<span class="number">1</span>].build();</span><br><span class="line">totsz = mn = n; </span><br><span class="line">getroot(<span class="number">1</span>, <span class="number">0</span>);</span><br><span class="line">DFS(rt);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%lld\n"</span>, ans);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>题目链接<a href="https://www.lydsy.com/JudgeOnline/problem.php?id=1921" target="_blank" rel="noopener">BZOJ1921</a></p>
</blockquote>
<h1 id="Solution"><a href="#Solution" class="headerlink" title="Solution"></a>Solution</h1><p>本题综合利用后缀自动机的呀</p>
<p>首先第一想法是怎么写暴力,从每个点为起点开始想下走,同时在SAM上走DAG,对于树上的每走到一个点,把答案加上其SAM对应节点的endpos大小即可,复杂度$ O(n^2) $</p>
<p>然后看到这种统计路径的题,回想到点分和DSU,先试试点分,那么需要做的是统计以经过某个点x的路径的贡献,假设这条路径是$ u \to x \to v $<br>点分的一般思路,如果可以单独算出$ u \to x $和$ x \to v $,那么拼在一起就好了<br>但是如何拼在一起呢<br>考虑可以在M串上统计,每个u到x在匹配的位置上加一个标记,那么最后的答案就是每个位置 向前匹配的标记和*向后匹配的标记和 之和<br>发现上面说的两种路径其中一种可以变成反串上的另一种,因此只需考虑一种就可以</p>
<p>然后就要求后缀自动机理解透彻了<br>网上的题解都说要在后缀树上统计$ u \to x $的路径贡献<br>然后我就想,为什么还要搞后缀树,直接在DAG上统计$ x \to v $的贡献不就可以啦,每次在后面加一个字符,方法同第一种暴力一样啊<br>然而在网上找不到这种做法,于是我就手画了好多后缀自动机和后缀树(因为画挂了很多次),然后找到了原因<br>如果我们走DAG来计算贡献,那么走到一个点时,要计入的是以$ x…v $为前缀的串的个数,但在DAG上走到一个位置,这个节点代表的字串可以有在$x…v$前面加了几个字符,然后就不满足要求了,但我们同样会计入答案。也就是说这样会统计多<br>但如果在后缀树上统计$ u \to x $,那么每次是在前面加一个字符,需要向儿子走,但可以保证之后走到的每一个点代表的所有串都是以$ u…x $为后缀的,就是当且仅当的意思,所以可以正确统计<br>这个还是要搞张图看看啊</p>
<p>后缀自动机一个重要性质:自动机的par树即是其反串的后缀树(所以par树也叫前缀树,前缀树是反串的后缀树,多好)<br>然后建后缀树时记录一下每个点到其儿子走的是哪个字符的转移边</p>
<p>这样点分的做法就完善了,由于在每个分治点都要在后缀树上将标记推到儿子并统计,复杂度是$ O(nlogn + nm) $,后面一部分成了瓶颈</p>
<p>前人想出了一个很神仙的操作,我们发现第一个解法(暴力)复杂度与m无关,因此可以考虑当分治的子树大小低于某个值时直接暴力统计即可<br>当这个值设为$ \sqrt n $时最优,复杂度可以控制在$ O((n + m)\sqrt n) $</p>
<p>写在最后<br>后缀系列算法真的好神啊,后缀自动机和后缀树的坑好深啊<br>主要是有各种各样的性质,互相转换又即为方便<br>然后通过这道题我还练熟了手画自动机[滑稽]</p>
</summary>
<category term="数据结构" scheme="http://rentingxutx.github.io/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/"/>
<category term="字符串" scheme="http://rentingxutx.github.io/categories/%E5%AD%97%E7%AC%A6%E4%B8%B2/"/>
<category term="后缀自动机" scheme="http://rentingxutx.github.io/categories/%E5%AD%97%E7%AC%A6%E4%B8%B2/%E5%90%8E%E7%BC%80%E8%87%AA%E5%8A%A8%E6%9C%BA/"/>
<category term="后缀树" scheme="http://rentingxutx.github.io/categories/%E5%AD%97%E7%AC%A6%E4%B8%B2/%E5%90%8E%E7%BC%80%E6%A0%91/"/>
<category term="点分治" scheme="http://rentingxutx.github.io/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E7%82%B9%E5%88%86%E6%B2%BB/"/>
<category term="后缀自动机" scheme="http://rentingxutx.github.io/tags/%E5%90%8E%E7%BC%80%E8%87%AA%E5%8A%A8%E6%9C%BA/"/>
<category term="后缀树" scheme="http://rentingxutx.github.io/tags/%E5%90%8E%E7%BC%80%E6%A0%91/"/>
<category term="点分治" scheme="http://rentingxutx.github.io/tags/%E7%82%B9%E5%88%86%E6%B2%BB/"/>
</entry>
<entry>
<title>BZOJ1920 [Ctsc2010]产品销售</title>
<link href="http://rentingxutx.github.io/2019/08/28/BZOJ1920-Ctsc2010-%E4%BA%A7%E5%93%81%E9%94%80%E5%94%AE/"/>
<id>http://rentingxutx.github.io/2019/08/28/BZOJ1920-Ctsc2010-产品销售/</id>
<published>2019-08-28T13:06:37.000Z</published>
<updated>2019-08-31T03:05:20.053Z</updated>
<content type="html"><![CDATA[<blockquote><p>题目链接:<a href="https://www.lydsy.com/JudgeOnline/problem.php?id=1920" target="_blank" rel="noopener">BZOJ1920</a></p></blockquote><h1><span id="solution">Solution</span></h1><p>对于30分的部分分,就是建出图后跑最小费用最大流<br>对于100分,就是模拟费用流的过程,中间用数据结构(线段树)维护</p><p>建图方式如下<br>$ s \to i $ 容量为U,费用为P<br>$ i \to i+1$ 容量为$ \infty$,费用为M<br>$ i+1 \to i$ 容量为$ \infty$,费用为C<br>$ i \to t$ 容量为D,费用为0<br>还有他们的反向边</p><p>考虑如何模拟费用流,首先得性质是,源点和汇点都是满流的,并且他们可以不退流,因此可以考虑枚举每个点i,分配他到汇点的流量使之满流<br>可以发现源点到i的流有两种:<br>$ s \to k \to k+1 \to … \to i-1 \to i ……(1)$<br>$ s \to k \to k-1 \to … \to i+1 \to i ……(2)$<br>我们每次选两种决策中费用较少的进行扩流,直到$D[i]$流满为止<br>也就是过程分为三步,<br>一、计算两种决策的代价<br>二、取较优的方式扩流,维护信息,更新答案,直到流满<br>三、从i移动到i+1,维护信息</p><h3><span id="决策2">决策(2)</span></h3><p>由于i是递增枚举的,所以从右到左的流只可能走C的正向边,可以不考虑M的反响边,代价为$ P[k] + sumC[k-1] - sumC[i] $,前面只与k有关,可以先排好序,每次跳过非法的($k \le i || U[k]==0$)的k就可以直接询问</p><p>扩流的话,流量为$flow = min(U[k], D[i])$,将$[i, k-1]$的区间流量加上$flow$即可</p><h3><span id="决策1">决策(1)</span></h3><p>M的正向边有无限容量,但代价大,C的反向边代价小(为负),但有容量限制,为路径上所有正向边流量的最小值,因此前面每条边的代价有两种情况,$-C $或$ +M$<br>如果可以对C的流量进行维护的话,那么就可以维护正确的代价,进而求最小值<br>这个维护,需要在流量从0边成非0时设置代价为$-C$,在其又退回0后将代价加上$C+M$,可以发现每条边最多在右边时变大,在左边时变小,因此只有这两个过程就够了</p><p>扩流同上</p><p>考虑这个0的维护,如果最开始初值设为0的话,那么很难区分0到底是初值还是后来减回来的,所以一个比较好的办法是初值设为inf<br>在没有用到它的时候默认inf就是初值的0<br>然后会面临在决策(2)的时候,流量被区间加,这时需要先把所有inf设为0,再区间加,可以发现每个inf最多被设为0一次,可以用并查集优化,每次跳到下一个inf的位置即可<br>然后面临在决策(1)时区间减,会出现减到0的情况这标志着要修改代价了,只有在这个时候会真正出现0。修改完代价后我们立刻把0再变回inf,代表它已经没有流量了。之所以他可以改成inf而不会继续对区间的容量有所限制,是因为我们已经修改过费用了,新的容量正好对应着新的费用。<br>这样就完美解决了最棘手的问题。</p><h3><span id="维护的信息">维护的信息</span></h3><p>需要维护C的流量flow,支持区间加减,查询最小值及其位置,以及获得每个0的位置,单点置为某值。<br>还要维护i之前每个点到i的代价(费用)cost,支持区间加减,查询最小值及其位置,单点置为某值<br>实际上操作是类似的,可以写成一个结构体的两个对象</p><p>具体怎么维护</p><p>在$cost[1…i-1] $中查询最小值和位置k,作为决策(1)<br>从排序好的那个序列首取出合法位置k,作为决策(2)<br>判断用那个决策,<br>对于(1)在$ flow[k…i-1] $中查询最小值,和U和D取min,得到流量,将$ flow[k…i-1] $区间减这个流量。在$flow$同样的区间递归找到每个为0的位置j,在$cost[1…j]$中区间加$ C[j]+M[j] $<br>对于(2),取U和D的min作为流量,$flow$区间加流量,加之前跳并查集将inf改为0<br>直到D[i]为0<br>将$cost[1…i]$区间加P[i](前提是U[i]不为0)<br>将$cost[1…i]$区间加M[i]或-C[i],取决于C[i]是否有流量,这是i对于前面每个点贡献的费用</p><p>然后终于结束了</p><a id="more"></a><h1><span id="code">Code</span></h1><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> rep(i, a, b) for(int i(a), i##_END(b); i <= i##_END; i++)</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> drep(i, a, b) for(int i(a), i##_END(b); i >= i##_END; i--);</span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> maxn = <span class="number">100010</span>;</span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">long</span> <span class="keyword">long</span> ll;</span><br><span class="line"><span class="keyword">typedef</span> pair<<span class="keyword">int</span>, <span class="keyword">int</span>> pii;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> inf = <span class="number">0x3f3f3f3f</span>;</span><br><span class="line"><span class="keyword">int</span> n, D[maxn], U[maxn], P[maxn], M[maxn], C[maxn], fa[maxn], sumC[maxn];</span><br><span class="line">pii rcs[maxn];</span><br><span class="line">ll ans;</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">get</span><span class="params">(<span class="keyword">int</span> x)</span> </span>{ <span class="keyword">return</span> fa[x] == x ? x : fa[x] = get(fa[x]); } </span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">SegmentTree</span>{</span></span><br><span class="line"><span class="keyword">int</span> mn[maxn * <span class="number">4</span>], pos[maxn * <span class="number">4</span>], tag[maxn * <span class="number">4</span>];</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">build</span><span class="params">(<span class="keyword">int</span> p, <span class="keyword">int</span> l, <span class="keyword">int</span> r, <span class="keyword">int</span> x)</span> </span>{</span><br><span class="line"><span class="keyword">if</span>(l == r) { mn[p] = x; pos[p] = l; tag[p] = <span class="number">0</span>; <span class="keyword">return</span>; }</span><br><span class="line"><span class="keyword">int</span> mid = (l + r) >> <span class="number">1</span>;</span><br><span class="line">build(p*<span class="number">2</span>, l, mid, x); build(p*<span class="number">2</span>+<span class="number">1</span>, mid + <span class="number">1</span>, r, x);</span><br><span class="line">mn[p] = min(mn[p*<span class="number">2</span>], mn[p*<span class="number">2</span>+<span class="number">1</span>]);</span><br><span class="line"><span class="keyword">if</span>(mn[p] == mn[p*<span class="number">2</span>]) pos[p] = pos[p*<span class="number">2</span>];</span><br><span class="line"><span class="keyword">else</span> pos[p] = pos[p*<span class="number">2</span>+<span class="number">1</span>];</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">pushdown</span><span class="params">(<span class="keyword">int</span> p)</span> </span>{</span><br><span class="line"><span class="keyword">if</span>(!tag[p]) <span class="keyword">return</span>; </span><br><span class="line">mn[p*<span class="number">2</span>] += tag[p]; mn[p*<span class="number">2</span>+<span class="number">1</span>] += tag[p];</span><br><span class="line">tag[p*<span class="number">2</span>] += tag[p]; tag[p*<span class="number">2</span>+<span class="number">1</span>] += tag[p];</span><br><span class="line">tag[p] = <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">change</span><span class="params">(<span class="keyword">int</span> p, <span class="keyword">int</span> l, <span class="keyword">int</span> r, <span class="keyword">int</span> L, <span class="keyword">int</span> R, <span class="keyword">int</span> x)</span> </span>{</span><br><span class="line"><span class="keyword">if</span>(L <= l && r <= R) { mn[p] += x; tag[p] += x; <span class="keyword">return</span>; }</span><br><span class="line">pushdown(p);</span><br><span class="line"><span class="keyword">int</span> mid = (l + r) >> <span class="number">1</span>;</span><br><span class="line"><span class="keyword">if</span>(L <= mid) change(p*<span class="number">2</span>, l, mid, L, R, x);</span><br><span class="line"><span class="keyword">if</span>(R > mid) change(p*<span class="number">2</span>+<span class="number">1</span>, mid + <span class="number">1</span>, r, L, R, x);</span><br><span class="line">mn[p] = min(mn[p*<span class="number">2</span>], mn[p*<span class="number">2</span>+<span class="number">1</span>]);</span><br><span class="line"><span class="keyword">if</span>(mn[p] == mn[p*<span class="number">2</span>]) pos[p] = pos[p*<span class="number">2</span>];</span><br><span class="line"><span class="keyword">else</span> pos[p] = pos[p*<span class="number">2</span>+<span class="number">1</span>];</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">change</span><span class="params">(<span class="keyword">int</span> p, <span class="keyword">int</span> l, <span class="keyword">int</span> r, <span class="keyword">int</span> u, <span class="keyword">int</span> x)</span> </span>{</span><br><span class="line"><span class="keyword">if</span>(l == r) { mn[p] = x; <span class="keyword">return</span>; }</span><br><span class="line">pushdown(p);</span><br><span class="line"><span class="keyword">int</span> mid = (l + r) >> <span class="number">1</span>;</span><br><span class="line"><span class="keyword">if</span>(u <= mid) change(p*<span class="number">2</span>, l, mid, u, x);</span><br><span class="line"><span class="keyword">else</span> change(p*<span class="number">2</span>+<span class="number">1</span>, mid + <span class="number">1</span>, r, u, x);</span><br><span class="line">mn[p] = min(mn[p*<span class="number">2</span>], mn[p*<span class="number">2</span>+<span class="number">1</span>]);</span><br><span class="line"><span class="keyword">if</span>(mn[p] == mn[p*<span class="number">2</span>]) pos[p] = pos[p*<span class="number">2</span>];</span><br><span class="line"><span class="keyword">else</span> pos[p] = pos[p*<span class="number">2</span>+<span class="number">1</span>];</span><br><span class="line">}</span><br><span class="line"><span class="function">pii <span class="title">query</span><span class="params">(<span class="keyword">int</span> p, <span class="keyword">int</span> l, <span class="keyword">int</span> r, <span class="keyword">int</span> L, <span class="keyword">int</span> R)</span> </span>{</span><br><span class="line"><span class="keyword">if</span>(L <= l && r <= R) <span class="keyword">return</span> make_pair(mn[p], pos[p]);</span><br><span class="line">pushdown(p);</span><br><span class="line"><span class="keyword">int</span> mid = (l + r) >> <span class="number">1</span>; <span class="function">pii <span class="title">res</span><span class="params">(inf, <span class="number">0</span>)</span></span>;</span><br><span class="line"><span class="keyword">if</span>(L <= mid) res = min(res, query(p*<span class="number">2</span>, l, mid, L, R));</span><br><span class="line"><span class="keyword">if</span>(R > mid) res = min(res, query(p*<span class="number">2</span>+<span class="number">1</span>, mid + <span class="number">1</span>, r, L, R));</span><br><span class="line"><span class="keyword">return</span> res;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">reset</span><span class="params">(<span class="keyword">int</span> p, <span class="keyword">int</span> l, <span class="keyword">int</span> r, <span class="keyword">int</span> L, <span class="keyword">int</span> R, SegmentTree &tar)</span> </span>{</span><br><span class="line"><span class="keyword">if</span>(l == r) { tar.change(<span class="number">1</span>, <span class="number">1</span>, n, <span class="number">1</span>, l, C[l] + M[l]); mn[p] = inf; <span class="keyword">return</span>; }</span><br><span class="line">pushdown(p);</span><br><span class="line"><span class="keyword">int</span> mid = (l + r) >> <span class="number">1</span>;</span><br><span class="line"><span class="keyword">if</span>(L <= mid && mn[p*<span class="number">2</span>] == <span class="number">0</span>) reset(p*<span class="number">2</span>, l, mid, L, R, tar);</span><br><span class="line"><span class="keyword">if</span>(R > mid && mn[p*<span class="number">2</span>+<span class="number">1</span>] == <span class="number">0</span>) reset(p*<span class="number">2</span>+<span class="number">1</span>, mid + <span class="number">1</span>, r, L, R, tar);</span><br><span class="line">mn[p] = min(mn[p*<span class="number">2</span>], mn[p*<span class="number">2</span>+<span class="number">1</span>]);</span><br><span class="line"><span class="keyword">if</span>(mn[p] == mn[p*<span class="number">2</span>]) pos[p] = pos[p*<span class="number">2</span>];</span><br><span class="line"><span class="keyword">else</span> pos[p] = pos[p*<span class="number">2</span>+<span class="number">1</span>];</span><br><span class="line">}</span><br><span class="line">}flow, cost;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">freopen(<span class="string">"product.in"</span>, <span class="string">"r"</span>, <span class="built_in">stdin</span>);</span><br><span class="line">freopen(<span class="string">"product.out"</span>, <span class="string">"w"</span>, <span class="built_in">stdout</span>);</span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%d"</span>, &n); </span><br><span class="line">rep(i, <span class="number">1</span>, n) <span class="built_in">scanf</span>(<span class="string">"%d"</span>, &D[i]);</span><br><span class="line">rep(i, <span class="number">1</span>, n) <span class="built_in">scanf</span>(<span class="string">"%d"</span>, &U[i]);</span><br><span class="line">rep(i, <span class="number">1</span>, n) <span class="built_in">scanf</span>(<span class="string">"%d"</span>, &P[i]);</span><br><span class="line">rep(i, <span class="number">1</span>, n - <span class="number">1</span>) <span class="built_in">scanf</span>(<span class="string">"%d"</span>, &M[i]);</span><br><span class="line">rep(i, <span class="number">1</span>, n - <span class="number">1</span>) <span class="built_in">scanf</span>(<span class="string">"%d"</span>, &C[i]);</span><br><span class="line">rep(i, <span class="number">1</span>, n) fa[i] = i, sumC[i] = sumC[i<span class="number">-1</span>] + C[i], rcs[i] = make_pair(P[i] + sumC[i - <span class="number">1</span>], i);</span><br><span class="line">sort(rcs + <span class="number">1</span>, rcs + <span class="number">1</span> + n);</span><br><span class="line"><span class="keyword">int</span> rpos = <span class="number">1</span>;</span><br><span class="line">flow.build(<span class="number">1</span>, <span class="number">1</span>, n, inf); cost.build(<span class="number">1</span>, <span class="number">1</span>, n, <span class="number">0</span>);</span><br><span class="line">rep(i, <span class="number">1</span>, n) {</span><br><span class="line"><span class="comment">//cout << i << endl;</span></span><br><span class="line"><span class="keyword">while</span>(D[i]) {</span><br><span class="line">pii lcost = (i > <span class="number">1</span> ? cost.query(<span class="number">1</span>, <span class="number">1</span>, n, <span class="number">1</span>, i - <span class="number">1</span>) : make_pair(inf, <span class="number">0</span>)), rcost;</span><br><span class="line"><span class="keyword">while</span>(rpos <= n && (rcs[rpos].second < i || !U[rcs[rpos].second])) ++rpos;</span><br><span class="line"><span class="keyword">if</span>(rpos <= n) rcost = rcs[rpos], rcost.first -= sumC[i - <span class="number">1</span>];</span><br><span class="line"><span class="keyword">else</span> rcost = make_pair(inf, <span class="number">0</span>);</span><br><span class="line"><span class="comment">//cout << lcost.second << ' ' << rcost.second << endl;</span></span><br><span class="line"><span class="keyword">if</span>(lcost <= rcost) {</span><br><span class="line"><span class="keyword">int</span> k = lcost.second;</span><br><span class="line">pii flowleft = flow.query(<span class="number">1</span>, <span class="number">1</span>, n, k, i - <span class="number">1</span>);</span><br><span class="line"><span class="keyword">int</span> nflow = min(flowleft.first, min(U[k], D[i]));</span><br><span class="line">flow.change(<span class="number">1</span>, <span class="number">1</span>, n, k, i - <span class="number">1</span>, -nflow);</span><br><span class="line">D[i] -= nflow; U[k] -= nflow;</span><br><span class="line">ans += <span class="number">1l</span>l * nflow * lcost.first;</span><br><span class="line"><span class="keyword">if</span>(!U[k]) cost.change(<span class="number">1</span>, <span class="number">1</span>, n, k, inf);</span><br><span class="line">flow.reset(<span class="number">1</span>, <span class="number">1</span>, n, k, i - <span class="number">1</span>, cost);</span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line"><span class="keyword">int</span> k = rcost.second;</span><br><span class="line"><span class="keyword">int</span> nflow = min(U[k], D[i]);</span><br><span class="line">D[i] -= nflow; U[k] -= nflow;</span><br><span class="line">ans += <span class="number">1l</span>l * nflow * rcost.first;</span><br><span class="line"><span class="keyword">int</span> j = get(i);</span><br><span class="line"><span class="keyword">while</span>(j <= k - <span class="number">1</span>) {</span><br><span class="line"><span class="keyword">int</span> fy = get(j + <span class="number">1</span>);</span><br><span class="line">flow.change(<span class="number">1</span>, <span class="number">1</span>, n, j, <span class="number">0</span>);</span><br><span class="line">fa[j] = fy; j = get(j);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span>(k > i) flow.change(<span class="number">1</span>, <span class="number">1</span>, n, i, k - <span class="number">1</span>, nflow);</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span>(i == n) <span class="keyword">break</span>;</span><br><span class="line">cost.change(<span class="number">1</span>, <span class="number">1</span>, n, i, U[i] ? P[i] : inf);</span><br><span class="line">cost.change(<span class="number">1</span>, <span class="number">1</span>, n, <span class="number">1</span>, i, fa[i] == i ? M[i] : -C[i]);</span><br><span class="line">}</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%lld\n"</span>, ans);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>题目链接:<a href="https://www.lydsy.com/JudgeOnline/problem.php?id=1920" target="_blank" rel="noopener">BZOJ1920</a></p>
</blockquote>
<h1 id="Solution"><a href="#Solution" class="headerlink" title="Solution"></a>Solution</h1><p>对于30分的部分分,就是建出图后跑最小费用最大流<br>对于100分,就是模拟费用流的过程,中间用数据结构(线段树)维护</p>
<p>建图方式如下<br>$ s \to i $ 容量为U,费用为P<br>$ i \to i+1$ 容量为$ \infty$,费用为M<br>$ i+1 \to i$ 容量为$ \infty$,费用为C<br>$ i \to t$ 容量为D,费用为0<br>还有他们的反向边</p>
<p>考虑如何模拟费用流,首先得性质是,源点和汇点都是满流的,并且他们可以不退流,因此可以考虑枚举每个点i,分配他到汇点的流量使之满流<br>可以发现源点到i的流有两种:<br>$ s \to k \to k+1 \to … \to i-1 \to i ……(1)$<br>$ s \to k \to k-1 \to … \to i+1 \to i ……(2)$<br>我们每次选两种决策中费用较少的进行扩流,直到$D[i]$流满为止<br>也就是过程分为三步,<br>一、计算两种决策的代价<br>二、取较优的方式扩流,维护信息,更新答案,直到流满<br>三、从i移动到i+1,维护信息</p>
<h3 id="决策(2)"><a href="#决策(2)" class="headerlink" title="决策(2)"></a>决策(2)</h3><p>由于i是递增枚举的,所以从右到左的流只可能走C的正向边,可以不考虑M的反响边,代价为$ P[k] + sumC[k-1] - sumC[i] $,前面只与k有关,可以先排好序,每次跳过非法的($k \le i || U[k]==0$)的k就可以直接询问</p>
<p>扩流的话,流量为$flow = min(U[k], D[i])$,将$[i, k-1]$的区间流量加上$flow$即可</p>
<h3 id="决策(1)"><a href="#决策(1)" class="headerlink" title="决策(1)"></a>决策(1)</h3><p>M的正向边有无限容量,但代价大,C的反向边代价小(为负),但有容量限制,为路径上所有正向边流量的最小值,因此前面每条边的代价有两种情况,$-C $或$ +M$<br>如果可以对C的流量进行维护的话,那么就可以维护正确的代价,进而求最小值<br>这个维护,需要在流量从0边成非0时设置代价为$-C$,在其又退回0后将代价加上$C+M$,可以发现每条边最多在右边时变大,在左边时变小,因此只有这两个过程就够了</p>
<p>扩流同上</p>
<p>考虑这个0的维护,如果最开始初值设为0的话,那么很难区分0到底是初值还是后来减回来的,所以一个比较好的办法是初值设为inf<br>在没有用到它的时候默认inf就是初值的0<br>然后会面临在决策(2)的时候,流量被区间加,这时需要先把所有inf设为0,再区间加,可以发现每个inf最多被设为0一次,可以用并查集优化,每次跳到下一个inf的位置即可<br>然后面临在决策(1)时区间减,会出现减到0的情况这标志着要修改代价了,只有在这个时候会真正出现0。修改完代价后我们立刻把0再变回inf,代表它已经没有流量了。之所以他可以改成inf而不会继续对区间的容量有所限制,是因为我们已经修改过费用了,新的容量正好对应着新的费用。<br>这样就完美解决了最棘手的问题。</p>
<h3 id="维护的信息"><a href="#维护的信息" class="headerlink" title="维护的信息"></a>维护的信息</h3><p>需要维护C的流量flow,支持区间加减,查询最小值及其位置,以及获得每个0的位置,单点置为某值。<br>还要维护i之前每个点到i的代价(费用)cost,支持区间加减,查询最小值及其位置,单点置为某值<br>实际上操作是类似的,可以写成一个结构体的两个对象</p>
<p>具体怎么维护</p>
<p>在$cost[1…i-1] $中查询最小值和位置k,作为决策(1)<br>从排序好的那个序列首取出合法位置k,作为决策(2)<br>判断用那个决策,<br>对于(1)在$ flow[k…i-1] $中查询最小值,和U和D取min,得到流量,将$ flow[k…i-1] $区间减这个流量。在$flow$同样的区间递归找到每个为0的位置j,在$cost[1…j]$中区间加$ C[j]+M[j] $<br>对于(2),取U和D的min作为流量,$flow$区间加流量,加之前跳并查集将inf改为0<br>直到D[i]为0<br>将$cost[1…i]$区间加P[i](前提是U[i]不为0)<br>将$cost[1…i]$区间加M[i]或-C[i],取决于C[i]是否有流量,这是i对于前面每个点贡献的费用</p>
<p>然后终于结束了</p>
</summary>
<category term="数据结构" scheme="http://rentingxutx.github.io/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/"/>
<category term="图论" scheme="http://rentingxutx.github.io/categories/%E5%9B%BE%E8%AE%BA/"/>
<category term="网络流" scheme="http://rentingxutx.github.io/categories/%E5%9B%BE%E8%AE%BA/%E7%BD%91%E7%BB%9C%E6%B5%81/"/>
<category term="线段树" scheme="http://rentingxutx.github.io/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E7%BA%BF%E6%AE%B5%E6%A0%91/"/>
<category term="费用流" scheme="http://rentingxutx.github.io/tags/%E8%B4%B9%E7%94%A8%E6%B5%81/"/>
<category term="线段树" scheme="http://rentingxutx.github.io/tags/%E7%BA%BF%E6%AE%B5%E6%A0%91/"/>
<category term="模拟" scheme="http://rentingxutx.github.io/tags/%E6%A8%A1%E6%8B%9F/"/>
</entry>
</feed>