-
Notifications
You must be signed in to change notification settings - Fork 0
/
local-search.xml
407 lines (195 loc) · 364 KB
/
local-search.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
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>MIT 6.S081 lab4:traps | 陷入</title>
<link href="/2024/02/29/MIT-6.S081-lab04/"/>
<url>/2024/02/29/MIT-6.S081-lab04/</url>
<content type="html"><![CDATA[<h1 id="lab4---traps">lab4 - traps</h1><h2 id="前置准备">前置准备</h2><p>主要内容为探索陷阱处理机制。</p><p>根据<ahref="https://pdos.csail.mit.edu/6.828/2021/schedule.html">课程官网</a>的要求,需要阅读完教材的第四章<spanclass="math inline">\(Page \spacetables\)</span>。并且读懂下列源代码:<code>kernel/memlayout.h</code>,<code>kernel/vm.c</code>, <code>kernel/kalloc.c</code>,<code>kernel/riscv.h</code>, <code>user/exec.c</code>。</p><p>相关笔记参考: <ahref="https://gejxd.github.io/2024/02/27/MIT-6.S081-lab04%20Notes/">lecture4 Notes</a></p><p>将<code>pgtbl</code>分支所有内容都上传仓库后,执行下列命令来切换分支<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">git fetch<br>git checkout traps<br>make clean<br></code></pre></td></tr></table></figure></p><p>此外,新建一个<code>traps_dev</code>分支来进行实际实验。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git checkout -b traps_dev<br></code></pre></td></tr></table></figure><p>在<code>traps_dev</code>中每通过一个作业的测试,提交(gitcommit)你的代码,并将所做的修改合并(git merge)到util中,然后提交(gitpush)到github。</p><figure class="highlight bash"><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></pre></td><td class="code"><pre><code class="hljs bash">git add .<br>git commit -m <span class="hljs-string">"xxxxxxxx"</span><br>git checkout traps<br>git merge traps_dev<br>git push github traps:traps<br></code></pre></td></tr></table></figure><h2 id="risc-v-assembly">RISC-V assembly</h2><p>在本部分中将给出一段 RISC-V汇编代码,通过阅读代码我们要回答几个问题,并把答案存储在主目录下的<code>answers-traps.txt</code>下。</p><p>运行 <code>make fs.img</code>后会编译<code>user/call.c</code>,并生成<code>user/call.asm</code>。我们需要观察<code>call.asm</code>下的<code>g</code>、<code>f</code>、<code>main</code>函数。</p><p>RISC-V 参考文档:<ahref="https://drive.google.com/file/d/1uviu1nH-tScFfgrovvFCrj7Omv8tFtkp/view">RISC-Vunprivileged instructions</a> <ahref="https://drive.google.com/file/d/17GeetSnT5wW3xNuAHI95-SI1gPGd5sJ_/view">RISC-Vprivileged instructions</a></p><blockquote><p>哪些寄存器包含函数的参数?例如,在 <code>main</code> 调用<code>printf</code> 时,哪个寄存器保存 <spanclass="math inline">\(13\)</span>?</p></blockquote><p>查阅<code>Calling conventions</code>手册,可以发现 <spanclass="math inline">\(a_0 \rightarrowa_7\)</span>为函数参数和返回值寄存器。</p><figure><img src="/img/6.S081/Lab-04/riscv-caller.png"alt="RISC-V常用寄存器及使用约定" /><figcaption aria-hidden="true">RISC-V常用寄存器及使用约定</figcaption></figure><p>13属于第三个参数(第一个为formatstring,第二个为<code>f(8) + 1</code>)。所以存储在<spanclass="math inline">\(a_2\)</span>寄存器中。</p><blockquote><p>在 <code>main</code> 的汇编代码中,函数 <code>f</code>的调用在哪里?对 <code>g</code> 的调用在哪里?提示:编译器可能会内联函数)。</p></blockquote><p>我们先分析一下<code>g()</code>的汇编代码。 <figure class="highlight x86asm"><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></pre></td><td class="code"><pre><code class="hljs x86asm"><span class="hljs-keyword">int</span> g(<span class="hljs-keyword">int</span> x) {<br> <span class="hljs-number">0</span>:<span class="hljs-number">1141</span> addi<span class="hljs-built_in">sp</span>,<span class="hljs-built_in">sp</span>,-<span class="hljs-number">16</span><br> <span class="hljs-number">2</span>:e406 sdra,<span class="hljs-number">8</span>(<span class="hljs-built_in">sp</span>)<br> <span class="hljs-number">4</span>:e022 sds0,<span class="hljs-number">0</span>(<span class="hljs-built_in">sp</span>)<br> <span class="hljs-number">6</span>:<span class="hljs-number">0800</span> addis0,<span class="hljs-built_in">sp</span>,<span class="hljs-number">16</span><br> return x + <span class="hljs-number">3</span><span class="hljs-comment">;</span><br>}<br> <span class="hljs-number">8</span>:<span class="hljs-number">250d</span> addiwa0,a0,<span class="hljs-number">3</span><br><span class="hljs-symbol"> a:</span>60a2 ldra,<span class="hljs-number">8</span>(<span class="hljs-built_in">sp</span>)<br><span class="hljs-symbol"> c:</span><span class="hljs-number">6402</span> lds0,<span class="hljs-number">0</span>(<span class="hljs-built_in">sp</span>)<br><span class="hljs-symbol"> e:</span><span class="hljs-number">0141</span> addi<span class="hljs-built_in">sp</span>,<span class="hljs-built_in">sp</span>,<span class="hljs-number">16</span><br> <span class="hljs-number">10</span>:<span class="hljs-number">8082</span> <span class="hljs-keyword">ret</span><br></code></pre></td></tr></table></figure>首先将栈顶指针<code>sp</code>往下移动16字节,等价与要入栈两个元素。将<code>ra</code>,即caller进程的<code>pc</code>值,存入栈的第一个位置。将<code>s0</code>,即caller进程的其他寄存器保存地址,存放到第二个位置。</p><p>然后将<code>a0</code>的值加3,存储到<code>a0</code>寄存器中。然后从栈中恢复<code>ra</code>和<code>s0</code>的地址,此时CPU能返回原进程继续执行。然后<code>ret</code>指令将<code>a0</code>复制给原进程,即返回值。</p><p><code>f()</code>函数和<code>g()</code>大同小异,只是编译器将<code>return g(x)</code>直接展开为<code>x + 3</code>了。</p><p><code>main</code>函数中可以看到,直接将12写入<code>a1</code>,直接将13写入<code>a2</code>。所以推测直接将<code>f(8) + 1</code>计算在编译器计算出来,当常数写入了。</p><blockquote><p>printf 函数位于哪个地址?</p></blockquote><p>可以看到<code>jalr</code>跳转到了<code>ra + 1544</code>的地址,也就是<code>0x640</code>的地方。所以printf应该在这个位置。</p><blockquote><p>在 jalr 跳转至 main 函数的 printf 时,寄存器 ra 中有什么值?</p></blockquote><p>当程序进行跳转时,我们需要将 ra 寄存器存储的返回地址指向 printf执行结束后返回到主程序的位置,也就是当前位置 PC 加 4,也就是 0x38</p><h2 id="backtrace-moderate">Backtrace (moderate)</h2><h3 id="statement">Statement</h3><p>在调试过程中,回溯通常很有用:在发生错误时,堆栈上的函数调用列表。</p><blockquote><p>在 <code>kernel/printf.c</code> 中实现一个 <code>backtrace()</code>函数。在 <code>sys_sleep</code> 中插入对该函数的调用,然后运行<code>bttest</code>,调用 <code>sys_sleep</code>。输出结果如下</p></blockquote><figure class="highlight bash"><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></pre></td><td class="code"><pre><code class="hljs bash">backtrace:<br>0x0000000080002cda<br>0x0000000080002bb6<br>0x0000000080002898<br></code></pre></td></tr></table></figure><blockquote><p><code>bttest</code> 之后退出<code>qemu</code>。在终端中:地址可能略有不同,但如果运行<code>addr2line -e kernel/kernel</code>(或<code>riscv64-unknown-elf-addr2line -e kernel/kernel</code>)并剪切粘贴上述地址,则如下所示:<figure class="highlight bash"><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></pre></td><td class="code"><pre><code class="hljs bash">$ addr2line -e kernel/kernel<br> 0x0000000080002de2<br> 0x0000000080002f4a<br> 0x0000000080002bfc<br> Ctrl-D<br></code></pre></td></tr></table></figure>应该查阅类似下面的内容:<code>kernel/sysproc.c:74</code>、<code>kernel/syscall.c:224</code>、<code>kernel/trap.c:85</code>。</p></blockquote><h3 id="hints">Hints</h3><ul><li>在 <code>kernel/defs.h</code> 中添加 <code>backtrace</code>原型,以便在 <code>sys_sleep</code> 中调用 <code>backtrace</code>。</li><li>GCC 编译器会将当前执行函数的帧指针存储在寄存器 s0中。将以下函数添加到 <code>kernel/riscv.h</code>,并在回溯中调用该函数来读取当前帧指针。该函数使用内联汇编读取 s0。<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></pre></td><td class="code"><pre><code class="hljs C"><span class="hljs-type">static</span> <span class="hljs-keyword">inline</span> uint64<br><span class="hljs-title function_">r_fp</span><span class="hljs-params">()</span><br>{<br> uint64 x;<br> <span class="hljs-keyword">asm</span> <span class="hljs-title function_">volatile</span><span class="hljs-params">(<span class="hljs-string">"mv %0, s0"</span> : <span class="hljs-string">"=r"</span> (x) )</span>;<br> <span class="hljs-keyword">return</span> x;<br>}<br></code></pre></td></tr></table></figure></li><li><ahref="https://pdos.csail.mit.edu/6.828/2021/lec/l-riscv-slides.pdf">这些讲义</a>中有一张堆栈帧布局的图片。返回地址位于堆栈帧的帧指针的固定偏移量(-8)处,而保存的帧指针位于帧指针的固定偏移量(-16)处。</li><li>Xv6 为 xv6 内核中的每个堆栈分配一个 PAGE 对齐地址的页面。您可以使用<code>PGROUNDDOWN(fp)</code> 和 <code>PGROUNDUP(fp)</code>计算堆栈页面的顶部和底部地址(参见 <code>kernel/riscv.h</code>)。</li></ul><h3 id="analysis-solution">Analysis & Solution</h3><p>前两个提示说的很明白了,这里就跳过。</p><p>首先读出<code>s0</code>寄存器的值,即当前函数的栈指针。然后用类似链表遍历的方式,每次输出<code>return address</code>的值,然后移动到<code>prev frame</code>继续遍历即可。</p><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></pre></td><td class="code"><pre><code class="hljs C"><span class="hljs-type">void</span> <span class="hljs-title function_">backtrace</span><span class="hljs-params">(<span class="hljs-type">void</span>)</span> {<br> <span class="hljs-built_in">printf</span>(<span class="hljs-string">"backtrace:\n"</span>);<br> uint64 fp = r_fp();<br> <span class="hljs-keyword">while</span> (fp != PGROUNDUP(fp)) { <span class="hljs-comment">// until get to stack bottom</span><br> <span class="hljs-comment">// get return addr in current stack frame</span><br> uint64 ra = *(uint64*)(fp - <span class="hljs-number">8</span>);<br> <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%p\n"</span>, ra);<br> <span class="hljs-comment">// go to prev stack frame</span><br> fp = *(uint64*)(fp - <span class="hljs-number">16</span>);<br> }<br>}<br></code></pre></td></tr></table></figure><p>然后在<code>sys_sleep()</code>和<code>panic()</code>中调用<code>backtrace</code>。</p><p>运行结果。 <img src="/img/6.S081/Lab-04/run_backtrace.png" /> <imgsrc="/img/6.S081/Lab-04/backtrace.png" /></p><h2 id="alarm-hard">Alarm (Hard)</h2><p>咕咕咕先。说实话,题目我都没看懂().</p><p>大概就是实现 <span class="math inline">\(CPU\)</span>计时器,当一个进程使用 <span class="math inline">\(CPU\)</span>资源的时候,周期性的发出一个警告,类似于时间片轮转算法的简化版本。感觉没十几个小时弄不完,等有生之年吧。</p>]]></content>
<categories>
<category>6.S081</category>
</categories>
<tags>
<tag>6.S081</tag>
</tags>
</entry>
<entry>
<title>MIT 6.S081 第四章笔记 | 陷入指令和中断</title>
<link href="/2024/02/29/MIT-6.S081-lab04%20Notes/"/>
<url>/2024/02/29/MIT-6.S081-lab04%20Notes/</url>
<content type="html"><![CDATA[<h2 id="isa-assembly-language">4.1 ISA & Assembly Language</h2><p><span class="math inline">\(ISA\)</span>: <spanclass="math inline">\(Instruction \space Set\)</span>。</p><p><span class="math inline">\(workflow\)</span>: <spanclass="math inline">\(C \rightarrow Assembly(.S/.asm) \rightarrowbinary(object.o)\)</span></p><p>汇编语言没有明确的工作流,只是一行一行的执行指令。汇编语言是基于寄存器进行操作的,而不是内存。</p><p><span class="math inline">\(RISC-V \space vs \space x86\)</span></p><ul><li><spanclass="math inline">\(RISC-V\)</span>是精简指令集,指令数少,效率更高,不好维护。</li><li><spanclass="math inline">\(x86\)</span>:复杂指令集,指令很多并且可以实现复杂功能</li></ul><h2 id="calling-convention">4.2 Calling convention</h2><p>调用约定(<span class="math inline">\(calling \spaceconvention\)</span>)是规定子过程如何获取参数以及如何返回的方案,调用约定一般规定了:- 参数、返回值、返回地址等放置的位置(寄存器、栈或内存)。</p><p>RISC-V通过寄存器来传递参数,而不是栈。<span class="math inline">\(a_0\rightarrow a_7\)</span> 是<code>int</code>参数,<spanclass="math inline">\(f_0 \rightarrow f_7\)</span>是<code>float</code>参数。</p><ul><li>如何将调用子过程的准备工作与恢复现场的工作划分到调用者(<spanclass="math inline">\(caller\)</span>)与被调用者(<spanclass="math inline">\(callee\)</span>)身上。</li></ul><p>小于一个指针字(RISCV64中是8字节,RISCV32是4字节)的参数传入时将参数放在寄存器的最低位,因为RISC-V是小端系统,当2个指针字的参数传入时,低位的1个指针字放在偶数寄存器,比如a0上,高位的1个指针字放在奇数寄存器,比如a1上。当高于2个指针字的参数传入时以引用的方式传入。struct参数没有传到寄存器的部分将以栈的方式传入,sp栈指针将指向第一个没有传入到寄存器的参数。</p><p>从函数返回的值,如果是整数将放在a0和a1中,如果是小数将放置在fa0和fa1寄存器中。对于更大的返回值,将放置在内存中,<spanclass="math inline">\(caller\)</span>开辟这个内存,并且把指向这个内存的指针作为第一个参数传递给 <spanclass="math inline">\(callee\)</span></p><p>由 <span class="math inline">\(caller\)</span>保存的寄存器不会在函数调用之间被保存,又名易失性寄存器,如果要在过程调用后恢复该值,则调用方需要将这些寄存器压入堆栈或复制到其他位置,而<span class="math inline">\(callee\)</span>保存的寄存器会被保存,称为非易失性寄存器,可以期望这些寄存器在被调用者返回后保持相同的值。比如函数A调用了函数B,所有函数A保存的寄存器在函数B被调用后可以被B重写覆盖。</p><figure><img src="/img/6.S081/Lab-04/riscv-caller.png"alt="RISC-V常用寄存器及使用约定" /><figcaption aria-hidden="true">RISC-V常用寄存器及使用约定</figcaption></figure><h2 id="stack">4.3 Stack</h2><p>栈从高地址向低地址增长,每个大的<spanclass="math inline">\(box\)</span>叫一个<spanclass="math inline">\(stack \spaceframe\)</span>(栈帧),栈帧由函数调用来分配,每个栈帧大小不一定一样,但是栈帧的最高处一定是<spanclass="math inline">\(return \space address\)</span>。</p><p>sp是stack pointer,用于指向栈顶(低地址),保存在寄存器中</p><p>fp是framepointer,用于指向当前帧底部(高地址),保存在寄存器中,同时每个函数栈帧中保存了调用当前函数的函数(父函数)的fp(保存在toprev frame那一栏中)</p><p>这些栈帧都是由编译器编译生成的汇编文件生成的</p><figure><img src="/img/6.S081/Lab-04/riscv-stack.png" alt="RISC-V栈" /><figcaption aria-hidden="true">RISC-V栈</figcaption></figure><h2 id="trap">4.4 trap</h2><p>3种可能的情况使得CPU暂停对正常指令的执行,并强制将控制权转移到处理该事件的特殊代码:1.syscall,移交给kernel 2. exception,指令执行了非法操作 3.设备中断。以上情况合并称为trap。</p><p>trap应该对于被打断的指令是透明的,也就是说被打断的指令不应该知道这个地方产生了trap,产生trap之后现场应该得以恢复并继续执行被打断的指令。</p><p>xv6对trap的处理分为四个阶段:1. 对RISC-V CPU的硬件的一些操作 2.一些为运行kernel C语言文件做准备的汇编文件 3. 用C实现的traphandler(暂且翻译为异常处理程序) 4. system call / device-driver serviceroutine</p><p>通常对于user space的trap、kernelspace的trap和计时器中断会有不同的trap handler</p><h2 id="risc-v-trap-machinery">4.5 RISC-V trap machinery</h2><p>RISC-VCPU有一系列的控制寄存器可以通知kernel发生了trap,也可以由kernel写入来告诉CPU怎样处理trap。</p><ul><li><code>stvec</code>:异常处理程序的地址。由内核填入。</li><li><code>sepc</code>:保存trap发生时当前的<code>PC</code>寄存器值,之后<code>PC</code>的值被<code>stvec</code>覆盖。通过<code>sret</code>指令将<code>sepc</code>值也入<code>PC</code>,达到回复现场的作用。</li><li><code>scause</code>: 填入产生trap的原因,有CPU写入。</li><li><code>sscartch</code>: 异常处理程序使用的一组寄存器。</li><li><code>sstatus</code>:SIE位控制设备中断是否被开启,SPP位指示trap是来自内核还是用户程序。</li></ul><p>以上寄存器都只在监管模式被启动。</p><p>当发生除了计时器中断以外的其他类型的trap时,RISC-V将执行以下步骤:</p><ol type="1"><li>如果trap是一个设备产生的中断,而SIE又被清除的情况下,不做下方的任何动作。</li><li>清除SIE来disable一切中断。</li><li>把pc复制到sepc。</li><li>把当前的模式(user / supervisor)保存到SPP。</li><li>设置scause寄存器来指示产生trap的原因。</li><li>将当前的模式设置为supervisor。</li><li>将stvec的值复制到pc。</li><li>开始执行pc指向的trap handler的代码。</li></ol><p>注意CPU并没有切换到kernel页表,也没有切换到kernel栈,也不会保存除PC外的其他寄存器值。这些操作都应该由内核完成。</p><h2 id="traps-from-user-space">4.6 Traps from user space</h2><p>如果用户程序进行系统调用(调用<code>ecall</code>指令)、执行非法操作或者设备中断时,就会产生trap。</p><p>当userspace中发生trap时,会将<code>stvec</code>的值复制到<code>pc</code>,而此时<code>stvec</code>的值是<code>trampoline.S</code>中的<code>uservec</code>,因此跳转到<code>uservec</code>函数。该函数先保存一些现场的寄存器,恢复kernel栈指针、kernelpagetable到satp寄存器,再跳转到<code>usertrap</code>(<code>kernel/trap.c</code>)这个用户trap通用执行器。然后返回<code>usertrapret</code>(<code>kernel/trap.c</code>),跳回到<code>kernel/trampoline.S</code>,最后用<code>userret(kernel/trampoline.S)</code>通过<code>sret</code>跳回到userspace。</p><p>RISC-V在trap中不会改变页表,因此user pagetable必须有对<code>uservec</code>的mapping,<code>uservec</code>是<code>stvec</code>指向的trapvectorinstruction。uservec要切换satp到kernel页表,同时kernel页表中也要有和user页表中对uservec相同的映射。RISC-V将<code>uservec</code>保存在<code>trampoline</code>页中,并将<code>TRAMPOLINE</code>放在kernel页表和user页表的相同位置处(MAXVA)。</p>]]></content>
<categories>
<category>6.S081</category>
</categories>
<tags>
<tag>6.S081</tag>
</tags>
</entry>
<entry>
<title>MIT 6.S081 第五章笔记 | 页面故障</title>
<link href="/2024/02/29/MIT-6.S081-lab05%20Notes/"/>
<url>/2024/02/29/MIT-6.S081-lab05%20Notes/</url>
<content type="html"><![CDATA[<p>当视图访问 <span class="math inline">\(PTE_V\)</span> 为 <spanclass="math inline">\(0\)</span> 的页表, 或用户尝试访问 <spanclass="math inline">\(PTE_U\)</span> 为 <spanclass="math inline">\(0\)</span> / 内核尝试访问 $PTE_U 为 <spanclass="math inline">\(1\)</span> 的页表, 或者是其他违反了 <spanclass="math inline">\(PTE_R\)</span> 或 <spanclass="math inline">\(PTE_W\)</span> 等 <spanclass="math inline">\(flag\)</span> 的页表时就会出现 <spanclass="math inline">\(Page Faults\)</span>。</p><p>一共有三种 <span class="math inline">\(Page Faults\)</span>: 1. <spanclass="math inline">\(load \space page \space faults\)</span>:当<code>load</code>指令无法解析虚拟地址时发生。 2. <spanclass="math inline">\(store \space page \space faults\)</span>:当<code>store</code>指令无法解析虚拟地址时发生。 3. <spanclass="math inline">\(instructions \space page \space faults\)</span>:当无法解析一条指令所在的虚拟地址时发生。</p><p><span class="math inline">\(page \space faults\)</span>种类的代码存放在<code>scause</code>寄存器中,无法翻译的地址存放在<code>stval</code>寄存器中。</p><p>在xv6中对于 <span class="math inline">\(exception\)</span>一律都会将这个进程kill掉,但是实际上可以结合<spanclass="math inline">\(page \space faults\)</span> 实现一些功能。</p><ol type="1"><li>可以实现 <span class="math inline">\(\textbf{copy-on-write} \space\textbf{fork}\)</span>。在<code>fork()</code>时,一般都是将父进程的所有<spanclass="math inline">\(user \space memory\)</span>复制到子进程中,但是<code>fork</code>之后一般会直接进行<code>exec</code>,这就会导致复制过来的<span class="math inline">\(user \space memory\)</span>又被放弃掉。因此改进的思路是:子进程和父进程共享一个物理内存,但是mapping时将PTE_W置零,只有当子进程或者父进程的其中一个进程需要向这个地址写入时产生pagefault,此时才会进行copy。</li><li>可以实现 <span class="math inline">\(\textbf{lazy} \space\testbf{allocation}\)</span>。旧的<code>sbrk()</code>申请分配内存,但是申请的这些内存进程很可能不会全部用到,因此改进方案为:当进程调用<code>sbrk()</code>时,将修改<code>p->sz</code>,但是并不实际分配内存,并且将PTE_V置0。当在试图访问这些新的地址时发生pagefault再进行物理内存的分配。</li><li><span class="math inline">\(\textbf{paging} \space \textbf{from}\space\textbf{disk}\)</span>:当内存没有足够的物理空间时,可以先将数据存储在其他的存储介质(比如硬盘)上,将该地址的PTE设置为<spanclass="math inline">\(invalid\)</span>,使其成为一个驱逐页(<spanclass="math inline">\(evictedpage\)</span>)。当需要读或者写这个PTE时,产生Pagefault,然后在内存上分配一个物理地址,将这个硬盘上的evictedpage的内容写入到该内存上,设置PTE为valid并且引用到这个内存物理地址。</li></ol><p>这两节没有什么新东西,主要就是结合前面三章的内容灵活应用,讲解一些操作系统的优化。更多在于理解为什么现代操作系统要这么设计(一个虚拟内存玩出花来了属于是)。</p>]]></content>
<categories>
<category>6.S081</category>
</categories>
<tags>
<tag>6.S081</tag>
</tags>
</entry>
<entry>
<title>MIT 6.S081 lab3:page tables | 页表</title>
<link href="/2024/02/28/MIT-6.S081-lab03/"/>
<url>/2024/02/28/MIT-6.S081-lab03/</url>
<content type="html"><![CDATA[<h1 id="lab3---page-table">lab3 - page table</h1><h2 id="前置准备">前置准备</h2><p>主要内容为熟悉页表遍历以及地址转换。</p><p>根据<ahref="https://pdos.csail.mit.edu/6.828/2021/schedule.html">课程官网</a>的要求,需要阅读完教材的第三章<spanclass="math inline">\(Page \spacetables\)</span>。并且读懂下列源代码:<code>kernel/memlayout.h</code>,<code>kernel/vm.c</code>, <code>kernel/kalloc.c</code>,<code>kernel/riscv.h</code>, <code>user/exec.c</code>。</p><p>相关笔记参考: <ahref="https://gejxd.github.io/2024/02/27/MIT-6.S081-lab03%20Notes/">lecture3 Notes</a></p><p>将<code>syscall</code>分支所有内容都上传仓库后,执行下列命令来切换分支<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">git fetch<br>git checkout pgtbl<br>make clean<br></code></pre></td></tr></table></figure></p><p>此外,新建一个<code>pgtbl_dev</code>分支来进行实际实验。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git checkout -b pgtbl_dev<br></code></pre></td></tr></table></figure><p>在<code>pgtbl_dev</code>中每通过一个作业的测试,提交(gitcommit)你的代码,并将所做的修改合并(git merge)到util中,然后提交(gitpush)到github。</p><figure class="highlight bash"><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></pre></td><td class="code"><pre><code class="hljs bash">git add .<br>git commit -m <span class="hljs-string">"xxxxxxxx"</span><br>git checkout pgtbl<br>git merge pgtbl_dev<br>git push github pgtbl:pgtbl<br></code></pre></td></tr></table></figure><h2 id="speed-up-system-calls-easy">Speed up system calls (easy)</h2><h3 id="statement">Statement</h3><p>在部分操作系统中,会使用用户空间和内核空间之间一块只读的共享内存来进行特定数据的共享,以此来达到加速特定的系统调用的目的,这样就消除了与内核交互产生的开销。在本部分中,我们将实现对<code>getpid()</code>系统调用的优化。</p><blockquote><p>当一个进程被创建时,映射一块只读的页面在<code>USYSCALL</code>(一个虚拟地址,定义在<code>kernel/memlayout.h</code>)。在该内存页上我们需要存储一个叫<code>struct usyscall</code>的结构体(同样定义在<code>kernel/memlayout.h</code>),将其初始化为当前进程的<code>PID</code>。</p></blockquote><h3 id="hints">Hints</h3><ul><li>可以在<code>kernel/proc.c</code>中的<code>proc_pagetable</code>中处理内存映射问题。</li><li>注意处理访问标志位使得内存页对用户空间来说是只读的。</li><li><code>mappages()</code>在该实验中会十分有用。</li><li>不要忘记在<code>allocproc()</code>中分配和初始化<code>usyscall</code>。</li><li>确保在<code>freeproc()</code>中释放内存页。</li></ul><h3 id="analysis-solution">Analysis & Solution</h3><p>先看看<code>proc_pagetable()</code>,可以看到就是用<code>mappages()</code>来将虚拟地址映射到物理地址。权限只需要设置<code>PTE_R</code>保证能读取,<code>PTE_U</code>保证用户内存能访问即可。</p><p>然后看一眼<code>allocproc()</code>,就是调用<code>kalloc()</code>来个<code>struct proc</code>里面的各个部分分配空间。</p><p>最后在看看<code>freeproc()</code>,对应把<code>allocproc</code>分配的东西,该free的free,该置0的置0。</p><p>所以我们要做的就很明显了:</p><ul><li>创建进程时,多存储一个<code>struct usyscall</code>。</li><li>创建进程页面时,将<code>struct usyscall</code>映射到<code>USYSCALL</code>。</li><li>销毁进程时,将<code>struct usyscall</code>释放,并且清空页表。</li></ul><p>在<code>struct proc</code>添加<code>struct usyscall</code>成员变量。<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></pre></td><td class="code"><pre><code class="hljs C"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">proc</span> {</span><br> <span class="hljs-comment">// 省略其他</span><br> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">usyscall</span>* <span class="hljs-title">usyscall_info</span>;</span><br>}<br></code></pre></td></tr></table></figure></p><p>然后在<code>proc_pagetable()</code>中添加映射。</p><p>NOTE:<code>uvmumap</code>要把上两次map的页面也删除。</p><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></pre></td><td class="code"><pre><code class="hljs C">proc_pagetable() {<br> <span class="hljs-comment">// 省略其他</span><br> <span class="hljs-keyword">if</span> (mappages(pagetable, USYSCALL, PGSIZE, (uint64)(p->usyscall), PTE_R | PTR_U) < ) {<br> uvmunmap(pagetable, TRAMPOLINE, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>);<br> uvmunmap(pagetable, TRAPFRAME, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>);<br> uvmfree(pagetable, <span class="hljs-number">0</span>);<br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br> }<br>}<br></code></pre></td></tr></table></figure><p>然后在<code>allocproc</code>中分配内存,并且初始化。</p><p>NOTE:usyscall的分配要在<code>p->pagetable</code>分配前实现。不然页表会映射为空。</p><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></pre></td><td class="code"><pre><code class="hljs C"><span class="hljs-type">static</span> <span class="hljs-keyword">struct</span> proc* <span class="hljs-title function_">allocproc</span><span class="hljs-params">(<span class="hljs-type">void</span>)</span> {<br> <span class="hljs-comment">// 省略其他</span><br> <span class="hljs-keyword">if</span> ((p->usyscall_info = (<span class="hljs-keyword">struct</span> usyscall*)kalloc()) == <span class="hljs-number">0</span>) {<br> freeproc(p);<br> release(&p->lock);<br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br> }<br> p->usyspage->pid = p->pid;<br>}<br></code></pre></td></tr></table></figure><p>最后在<code>freeproc</code>中释放内存。 <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></pre></td><td class="code"><pre><code class="hljs C"><span class="hljs-type">static</span> <span class="hljs-type">void</span> <span class="hljs-title function_">freeproc</span><span class="hljs-params">(<span class="hljs-keyword">struct</span> proc *)</span> {<br> <span class="hljs-comment">// 省略其他</span><br> <span class="hljs-keyword">if</span> (p->usyscall_info) kfree((<span class="hljs-type">void</span>*)p->usyscall_info);<br> p->usyscall_info = <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></p><p>然后运行会喜提一个<code>panic: freewalk leaf</code>。翻一下<code>freewalk</code>这个函数,发现如果PTE_V没有设置,或者pte为空,就会有这个panic。<img src="/img/6.S081/Lab-03/panic.png" /></p><p>刚才只有<code>mappages</code>中修改到了页表。查看<code>mappages</code>函数的代码。由于并没有提示<code>panic:mappages remap</code>,说明PTE_V被正常设置。那问题只能是<code>freewalk</code>中的<code>pte</code>为空了。</p><p>一通检查,发现页表<code>freeproc</code>中的<code>uvmfree</code>只是把所有项都置为0,并不清空映射,所以映射出来就为空了。然后找到<code>proc_freepagetable()</code>这个函数(在<code>kernel/proc.h</code>中),删除<code>USYSCALL</code>的映射即可。<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></pre></td><td class="code"><pre><code class="hljs C"><span class="hljs-type">void</span> <span class="hljs-title function_">proc_freepagetable</span><span class="hljs-params">(<span class="hljs-type">pagetable_t</span> pagetable, uint64 sz)</span> {<br> <span class="hljs-comment">// 省略其他</span><br> uvmunmap(pagetable, USYSCALL, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>);<br>}<br></code></pre></td></tr></table></figure></p><p><img src="/img/6.S081/Lab-03/ugetpid.png" /></p><h2 id="print-a-page-table-easy">Print a page table (easy)</h2><h3 id="statement-1">Statement</h3><p>在本部分中,我们需要将 RISC-V的页表可视化,也就是实现一个页表内容的打印功能,作为后续调试的辅助工具。</p><blockquote><p>我们需要定义一个<code>vmprint</code>函数。该函数应该有一个参数<code>pagetable_t</code>,作为要可视化的页表,然后按下面的格式打印这个页表的信息。添加一行<code>if (p->pid == 1) vmprimt(p->pagetable)</code>在<code>exec.c</code>中,在返回<code>argc</code>之前输出第一个进程的页面信息。</p></blockquote><p>当你启动<code>xv6</code>的时候,<code>exec</code>完成内核加载时你应该会看到下面的信息:</p><figure class="highlight jboss-cli"><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></pre></td><td class="code"><pre><code class="hljs jboss-cli">page table 0x0000000087f6e000<br> <span class="hljs-string">..0</span>: pte 0x0000000021fda801 pa 0x0000000087f6a000<br> <span class="hljs-string">..</span> <span class="hljs-string">..0</span>: pte 0x0000000021fda401 pa 0x0000000087f69000<br> <span class="hljs-string">..</span> <span class="hljs-string">..</span> <span class="hljs-string">..0</span>: pte 0x0000000021fdac1f pa 0x0000000087f6b000<br> <span class="hljs-string">..</span> <span class="hljs-string">..</span> <span class="hljs-string">..1</span>: pte 0x0000000021fda00f pa 0x0000000087f68000<br> <span class="hljs-string">..</span> <span class="hljs-string">..</span> <span class="hljs-string">..2</span>: pte 0x0000000021fd9c1f pa 0x0000000087f67000<br> <span class="hljs-string">..255</span>: pte 0x0000000021fdb401 pa 0x0000000087f6d000<br> <span class="hljs-string">..</span> <span class="hljs-string">..511</span>: pte 0x0000000021fdb001 pa 0x0000000087f6c000<br> <span class="hljs-string">..</span> <span class="hljs-string">..</span> <span class="hljs-string">..509</span>: pte 0x0000000021fdd813 pa 0x0000000087f76000<br> <span class="hljs-string">..</span> <span class="hljs-string">..</span> <span class="hljs-string">..510</span>: pte 0x0000000021fddc07 pa 0x0000000087f77000<br> <span class="hljs-string">..</span> <span class="hljs-string">..</span> <span class="hljs-string">..511</span>: pte 0x0000000020001c0b pa 0x0000000080007000<br></code></pre></td></tr></table></figure><p>第一行表示了 <code>vmprint</code>传入的参数,也就是页表入口的地址。接下来的每一行都是 PTE,以及 PTE下可能存在的下级页表(学过算法的都应该看出来了这是一个递归)。我们用<code>..</code>来表示这个 PTE 的深度,最开始打印的数字是这个 PTE 在一个 4KB 内存页(共512 个 PTE)的编号,接下来会打印 PTE 的具体数值。</p><h3 id="hints-1">Hints</h3><ul><li>可以把<code>vmprint</code>实现在<code>kernel/vm.c</code>文件里面。</li><li>记得使用<code>kernel/riscv.h</code>中的宏定义,包括但不限于<code>PTE2PA</code>等,来方便你转换PTE到物理地址。</li><li>如果你无从下手,记得阅读<code>freewalk</code>函数。</li><li>在<code>kernel/defs.h</code>中添加<code>vmprint</code>的原型,一遍在<code>exec.c</code>中正确调用。</li><li>在<code>print</code>中使用<code>%p</code>来打印完整的64位地址信息。</li></ul><h3 id="analysis-solution-1">Analysis & Solution</h3><p>提示里面说了<code>freewalk</code>函数很重要,所以外面先读一下这个函数。</p><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><code class="hljs c"><span class="hljs-comment">// Recursively free page-table pages.</span><br><span class="hljs-comment">// All leaf mappings must already have been removed.</span><br><span class="hljs-type">void</span> <span class="hljs-title function_">freewalk</span><span class="hljs-params">(<span class="hljs-type">pagetable_t</span> pagetable)</span> {<br> <span class="hljs-comment">// there are 2^9 = 512 PTEs in a page table.</span><br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-number">512</span>; i++) {<br> <span class="hljs-type">pte_t</span> pte = pagetable[i];<br> <span class="hljs-keyword">if</span> ((pte & PTE_V) && (pte & (PTE_R | PTE_W | PTE_X)) == <span class="hljs-number">0</span>) {<br> <span class="hljs-comment">// this PTE points to a lower-level page table.</span><br> uint64 child = PTE2PA(pte);<br> freewalk((<span class="hljs-type">pagetable_t</span>)child);<br> pagetable[i] = <span class="hljs-number">0</span>;<br> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (pte & PTE_V) {<br> panic(<span class="hljs-string">"freewalk: leaf"</span>);<br> }<br> }<br> kfree((<span class="hljs-type">void</span> *)pagetable);<br>}<br></code></pre></td></tr></table></figure><p>首先传入了一个页表,并且遍历里面的所有项。如果<code>pte</code>不为空并且<code>PTE_V</code>标志位为1,说明这项页面存在,并且映射了一个虚拟内存到物理内存。如果<code>PTE_R</code>、<code>PTE_R</code>、<code>PRE_X</code>都为0,说明这个页面为高级页表,读取下一级页表的地址,递归调用<code>freewalk</code>来遍历。</p><p>emmmm,然后就没什么好说的了,照着上面加点输入输出就可以。</p><p><code>vmprint</code>:</p><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></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// lab3 - exercise 2</span><br><span class="hljs-comment">// print a page table</span><br><span class="hljs-type">void</span> <span class="hljs-title function_">vmprint</span><span class="hljs-params">(<span class="hljs-type">pagetable_t</span> pagetable, <span class="hljs-type">int</span> depth)</span> {<br> <span class="hljs-keyword">if</span> (depth == <span class="hljs-number">1</span>)<br> <span class="hljs-built_in">printf</span>(<span class="hljs-string">"page table %p\n"</span>, pagetable);<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>;i < <span class="hljs-number">512</span>;i ++) {<br> <span class="hljs-type">pte_t</span> pte = pagetable[i];<br> <span class="hljs-comment">// printf("%d: pte %p pa %p\n", i, pte, PTE2PA(pte));</span><br> <span class="hljs-keyword">if</span> (pte & PTE_V) {<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> j = <span class="hljs-number">1</span>;j <= depth;j ++)<br> <span class="hljs-built_in">printf</span>(<span class="hljs-string">"..%s"</span>, j == depth ? <span class="hljs-string">""</span> : <span class="hljs-string">" "</span>);<br> <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%d: pte %p pa %p\n"</span>, i, pte, PTE2PA(pte));<br> <span class="hljs-keyword">if</span> ((pte & (PTE_R | PTE_W | PTE_X)) == <span class="hljs-number">0</span>) {<br> <span class="hljs-type">pagetable_t</span> child = (<span class="hljs-type">pagetable_t</span>)PTE2PA(pte);<br> vmprint((<span class="hljs-type">pagetable_t</span>)child, depth + <span class="hljs-number">1</span>);<br> }<br> }<br> }<br>}<br></code></pre></td></tr></table></figure><p>然后在<code>kernel/defs.h</code>中添加原型:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// vm.c</span><br><span class="hljs-type">void</span> <span class="hljs-title function_">vmprint</span><span class="hljs-params">(<span class="hljs-type">pagetable_t</span> pagetable)</span>;<br></code></pre></td></tr></table></figure><p>在<code>exec.c</code>的<code>return</code>前插入代码:</p><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></pre></td><td class="code"><pre><code class="hljs C"><span class="hljs-keyword">if</span>(p->pid==<span class="hljs-number">1</span>) <br> vmprint(p->pagetable);<br><span class="hljs-keyword">return</span> argc; <span class="hljs-comment">// this ends up in a0, the first argument to main(argc, argv)</span><br></code></pre></td></tr></table></figure><p><img src="/img/6.S081/Lab-03/print.png" /></p><h2 id="detecting-which-pages-have-been-accessed">Detecting which pageshave been accessed</h2><h3 id="statement-2">Statement</h3><p>一些垃圾回收器(一种自动内存管理的形式),可以从已经被访问的内存(写或读)中获得信息。在这部分实验,你将要给xv6内核添加一个新特性,通过检查RISC-V页表中的访问页来给用户空间传递这一信息。每当RISC-V硬件解决TLB未命中问题的时候,都会在PTE的标志位中标记对应位。(即不用考虑TLB和页表潜在不同步问题)。</p><blockquote><p>你的目标是实现<code>pgaccess()</code>,一个用于报告当前进程哪些页面已被访问过的系统调用。它需要3个参数。首先,它需要第一个要检查的用户页面的起始虚拟地址。其次,它接受要检查的页面数。最后,它需要一个缓冲区的用户地址,以便将结果存储到位掩码(一种数据结构,每页使用一位,其中第一页对应的是最小有效位)中。如果在运行<code>pgtbltest</code> 时通过了 <code>pgaccess</code>测试用例,这部分实验将获得满分。</p></blockquote><h3 id="hints-2">Hints</h3><ul><li>首先在 <code>kernel/sysproc.c</code> 中实现<code>sys_pgaccess()</code>。</li><li>需要使用 <code>argaddr()</code> 和 <code>argint()</code>解析参数。</li><li>对于输出位掩码,在内核中存储一个临时缓冲区并在填入正确的位后将其拷贝给用户(通过<code>copyout()</code>)会更容易一些</li><li><code>kernel/vm.c</code> 中的 <code>walk()</code> 对于找到正确的 PTE非常有用。</li><li>需要在 <code>kernel/riscv.h</code> 中定义访问位<code>PTE_A</code>。请查阅 RISC-V 手册确定其值。</li><li>在检查 <code>PTE_A</code>是否被设置后,请务必将其清除。否则,将无法确定上次调用<code>pgaccess()</code> 后是否访问过页面(即该位将永远被设置)。</li><li><code>vmprint()</code> 可能会在调试页表时派上用场。</li></ul><h3 id="analysis-solution-2">Analysis & Solution</h3><p>大概意思可能和<code>cache</code>中的脏位差不多。检测到上一次调用<code>sys_pgaccess()</code>这段时间内,哪些页表项被访问过。可能完整的UNIX 系统中会用这个信息来实现TLB和页表的同步。</p><p>需要注意的是,PTE_A位是由RISC-V硬件来维护的,在xv6中则是由<code>qemu</code>模拟器来负责维护,我们不用考虑什么时候将PTE_A置为1。</p><p>还是教材上这张图, RISC-V硬件页表的标记位: <imgsrc="/img/6.S081/Lab-03/bitmask.png" /></p><p>可以看到 <span class="math inline">\(access\)</span> 标志位是第 <spanclass="math inline">\(6\)</span> 位(从 <spanclass="math inline">\(0\)</span> 数起,从左到右)。所以在<code>kernel/riscv.h</code>下面,添加一位PTE_A的定义。<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></pre></td><td class="code"><pre><code class="hljs C"><span class="hljs-meta">#<span class="hljs-keyword">define</span> PTE_V (1L << 0) <span class="hljs-comment">// valid</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> PTE_R (1L << 1)</span><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> PTE_W (1L << 2)</span><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> PTE_X (1L << 3)</span><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> PTE_U (1L <span class="hljs-string"><< 4) // 1 -></span> user can access</span><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> PTE_A (1L << 6) <span class="hljs-comment">// Lab pgtbl: Whether it has been visited</span></span><br></code></pre></td></tr></table></figure></p><p>按照lab-2的内容看看系统调用需要添加的东西,真良心全添加好了。所以我们只用考虑怎么来实现。在<code>kernel/sysproc.c</code>中 <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></pre></td><td class="code"><pre><code class="hljs C"><span class="hljs-meta">#<span class="hljs-keyword">ifdef</span> LAB_PGTBL</span><br><span class="hljs-type">int</span> <span class="hljs-title function_">sys_pgaccess</span><span class="hljs-params">(<span class="hljs-type">void</span>)</span> {<br> <span class="hljs-comment">// lab pgtbl: your code here.</span><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br><span class="hljs-meta">#<span class="hljs-keyword">endif</span></span><br></code></pre></td></tr></table></figure></p><p>我们用一个 <span class="math inline">\(64\)</span>位的整数来当作<code>bitmask</code>,一位表示一张页表,所以只能访问 <spanclass="math inline">\(64\)</span>张页表。即参数里的<code>pgnums</code>不能超过 <spanclass="math inline">\(64\)</span>。然后就是从用户进程页表中的<code>va</code>开始,往下访问<code>pgnums</code>张页表项,如果当前PTE的access位为1,对应的bitmask位也置为1。由于<code>sys_pgaccess()</code>也会访问页表,这个操作也会将PTE_A置为1,所以在标记完<code>bitmask</code>之后,需要将PTE_A重置。</p><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></pre></td><td class="code"><pre><code class="hljs C"><span class="hljs-type">int</span> <span class="hljs-title function_">sys_pgaccess</span><span class="hljs-params">(<span class="hljs-type">void</span>)</span> {<br> <span class="hljs-comment">// virtual address</span><br> uint64 va;<br> <span class="hljs-keyword">if</span> (argaddr(<span class="hljs-number">0</span>, &va) < <span class="hljs-number">0</span>) {<br> <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;<br> }<br><br> <span class="hljs-comment">// the number of page table to check.</span><br> <span class="hljs-type">int</span> pgnums;<br> <span class="hljs-keyword">if</span> (argint(<span class="hljs-number">1</span>, &pgnums) < <span class="hljs-number">0</span>) {<br> <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;<br> }<br><br> <span class="hljs-comment">// user virtual address</span><br> uint64 ua;<br> <span class="hljs-keyword">if</span> (argaddr(<span class="hljs-number">2</span>, &ua) < <span class="hljs-number">0</span>) {<br> <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;<br> }<br><br> uint64 bitmask = <span class="hljs-number">0</span>;<br> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">proc</span>* <span class="hljs-title">p</span> =</span> myproc();<br><br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < pgnums; i++) {<br> <span class="hljs-keyword">if</span> (va >= MAXVA) <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;<br> <span class="hljs-type">pte_t</span> *pte = walk(p->pagetable, va, <span class="hljs-number">0</span>);<br> <span class="hljs-keyword">if</span> ((*pte & PTE_V) && (*pte & PTE_A)) {<br> bitmask |= (<span class="hljs-number">1</span> << i);<br> *pte ^= PTE_A;<br> }<br> va += PGSIZE;<br> }<br><br> <span class="hljs-keyword">if</span> (copyout(p->pagetable, ua, (<span class="hljs-type">char</span>*)&bitmask, <span class="hljs-keyword">sizeof</span>(bitmask)) < <span class="hljs-number">0</span>) {<br> <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;<br> }<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><p>然后报错说<code>walk()</code>没被定义,看一眼<code>kernel/defs.h</code>,发现没有<code>walk</code>函数原型,所以添加一个即可。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs C"><span class="hljs-type">int</span> <span class="hljs-title function_">copyinstr</span><span class="hljs-params">(<span class="hljs-type">pagetable_t</span>, <span class="hljs-type">char</span>*, uint64, uint64)</span>;<br><span class="hljs-type">pte_t</span>* <span class="hljs-title function_">walk</span><span class="hljs-params">(<span class="hljs-type">pagetable_t</span>, uint64, <span class="hljs-type">int</span>)</span>;<br></code></pre></td></tr></table></figure><p><img src="/img/6.S081/Lab-03/sys_pgaccess.png" /></p><h2 id="submit-lab">Submit lab</h2><p>新建<code>time.txt</code>文件,输入一个整数表明完成所有实验的耗时。</p><p>运行<code>make grade</code>。 <imgsrc="/img/6.S081/Lab-03/grade.png" /></p><p>这里<code>usertests</code>会评测所有xv6的函数,耗时较长,如果用虚拟机或低配置机器的同学可以修改<code>grade-lab-pgtbl</code>里面的<code>timeout</code>,不然还没评测完就给你来一个血红色的<code>FAIL</code>。</p><p>运行<code>make handin</code>,根据提示将<code>API KEY</code>填入,提交完成。</p>]]></content>
<categories>
<category>6.S081</category>
</categories>
<tags>
<tag>6.S081</tag>
</tags>
</entry>
<entry>
<title>MIT 6.S081 第三章笔记 | 页表</title>
<link href="/2024/02/27/MIT-6.S081-lab03%20Notes/"/>
<url>/2024/02/27/MIT-6.S081-lab03%20Notes/</url>
<content type="html"><![CDATA[<p>页表让每个进程都拥有自己独立的虚拟内存,从而实现内存隔离。</p><h2 id="paging-hardware">3.1 Paging hardware</h2><p>用户和内核都只能操作虚拟地址(<span class="math inline">\(virtual\space address\)</span>),但是实际物理内存使用物理地址(<spanclass="math inline">\(physical \spaceaddress\)</span>)来索引。页表提供了虚拟地址到逻辑地址的转换。</p><p>xv6只使用了64位地址空间中的低39位,其中高<spanclass="math inline">\(27\)</span>位为页面号,低<spanclass="math inline">\(12\)</span>位为页内偏移,即<spanclass="math inline">\(4096(2^{12})\)</span>字节一个<spanclass="math inline">\(page\)</span>,同<code>kernel/riscv.h</code>中<code>PGSIZE</code>相同,一个进程的虚拟内存可以有<spanclass="math inline">\(2^{27}\)</span>个<spanclass="math inline">\(page\)</span>,对应到<spanclass="math inline">\(2^{27}\)</span>个页表项<spanclass="math inline">\((page \space table \space entries,PTEs)\)</span>。每个<span class="math inline">\(PTE\)</span>存储<spanclass="math inline">\(44\)</span>位的物理地址和10位标记,总共<spanclass="math inline">\(54\)</span>位,即一个PTE需要8字节来存储。每个物理地址高<span class="math inline">\(44\)</span> 位是PTE中存储, 后<spanclass="math inline">\(12\)</span>位用页内偏移。一个物理地址总共用<spanclass="math inline">\(56\)</span>位表示。</p><figure><img src="/img/6.S081/Lab-03/figure3-1.png"alt="Figure 3.1: RISC-V virtual and physical addresses, with a simplified logical page table." /><figcaption aria-hidden="true">Figure 3.1: RISC-V virtual and physicaladdresses, with a simplified logical page table.</figcaption></figure><p>RISC-V页表并不是整个存储在内存中的(因为很难找到空闲的整块内存存储),而是采用三级树结构,来使得页表空间可以动态分配和离散存储。每个页表就是一页。第一级页表是一个4096字节的页,包含了512个PTE(因为每个PTE需要8字节),每个PTE存储了下级页表的页物理地址。第二级列表由512个页构成(<spanclass="math inline">\(512 *4096字节\)</span>),第三级列表由512*512个页构成。因为每个进程虚拟地址的高27位用来确定PTE,对应到3级页表就是最高的9位确定一级页表PTE的位置(即偏移量),中间9位确定二级页表PTE的位置,最低9位确定三级页表PTE的位置。每一页内地址是连续的,但是不同页之间内存不一定连续。如下图所示。第一级根页表的物理地址存储在<code>satp</code>寄存器中,每个CPU核心拥有自己独立的<code>satp</code>。</p><figure><img src="/img/6.S081/Lab-03/figure3-2.png"alt="Figure 3.2: RISC-V address translation details." /><figcaption aria-hidden="true">Figure 3.2: RISC-V address translationdetails.</figcaption></figure><p>PTE flag可以告诉硬件这些相应的虚## 3.4 Physical memory allocation</p><p>xv6在运行时分配或释放页表、用户内存、内核栈、管道缓冲区等各种物理内存的不同用途。xv6中这些内存都分配在<strong>内核数据</strong>的末位和PHYSTOP之间,每次分配4096字节,即4KB空间。</p><p>分配和释放是通过对空闲页链表进行追踪完成的(<code>kernel/kalloc.c:struct kmem</code>),分配空间就是将一个页从链表中移除,释放空间就是将一页增加到链表中。</p><p>kernel的物理空间的分配函数在<code>kernel/kalloc.c</code>中,每个页在链表中的元素是<code>struct run</code>,每个run存储在空闲页本身中。这个空闲页的链表<code>freelist</code>由<code>spin lock</code>保护,包装在<code>struct kmem</code>中。</p><ul><li><code>kinit</code>:初始化所有空闲内存列表,在内核刚启动的时候调用。从<code>kernel end</code>到<code>PHYSTOP</code>之间的所有内存都按页清空,并存放在<code>freelist</code>中。</li><li><code>freerange</code>:将range中的每一个页面都调用一次<code>free()</code>来将其插入到<code>freelist</code>的末尾。</li></ul><h2 id="进程地址空间">3.5 进程地址空间</h2><h2 id="kernel-address-space">3.2 Kernel address space</h2><p><code>QEMU</code>会模拟一块从<code>0x80000000</code>开始的内存,到至少<code>0x88000000</code>。<code>0x80000000</code>以下的地址被视为直接与设备交互,而不是内存。## 3.4 Physical memory allocation</p><p>xv6在运行时分配或释放页表、用户内存、内核栈、管道缓冲区等各种物理内存的不同用途。xv6中这些内存都分配在<strong>内核数据</strong>的末位和PHYSTOP之间,每次分配4096字节,即4KB空间。</p><p>分配和释放是通过对空闲页链表进行追踪完成的(<code>kernel/kalloc.c:struct kmem</code>),分配空间就是将一个页从链表中移除,释放空间就是将一页增加到链表中。</p><p>kernel的物理空间的分配函数在<code>kernel/kalloc.c</code>中,每个页在链表中的元素是<code>struct run</code>,每个run存储在空闲页本身中。这个空闲页的链表<code>freelist</code>由<code>spin lock</code>保护,包装在<code>struct kmem</code>中。</p><ul><li><code>kinit</code>:初始化所有空闲内存列表,在内核刚启动的时候调用。从<code>kernel end</code>到<code>PHYSTOP</code>之间的所有内存都按页清空,并存放在<code>freelist</code>中。</li><li><code>freerange</code>:将range中的每一个页面都调用一次<code>free()</code>来将其插入到<code>freelist</code>的末尾。</li></ul><h2 id="进程地址空间-1">3.5 进程地址空间</h2><ul><li><code>trampoline page</code>在用户空间和内核空间中都在同一个虚拟地址。以便能在<code>user</code>和<code>kernel</code>间切换时方便的访问。</li><li>kernel stackpage:每个进程有一个自己的内核栈kstack,每个kstack下面有一个没有被映射的guardpage,guard page的作用是防止kstack溢出影响其他kstack。</li></ul><h2 id="code-creating-an-address-space">3.3 Code: creating an addressspace</h2><p>大多数和管理页表相关的代码都存放在<code>kernel/vm.c</code>中。核心结构体是<code>pagetable_t</code>,<strong>实际是一个指向一块8字节内存的指针</strong>(见<code>kernel/riscv.h</code>最后一行)。</p><p>重要的函数:</p><ul><li><code>walk</code>(<code>kernel/vm.c</code>):模拟RISC-V三级分页硬件。给定虚拟地址和页表,返回最终页表的PTE。</li><li><code>mappages</code>:给定一个页表,虚拟地址和物理地址,通过在页表中写入PTE来建立映射。</li><li><code>kvminit</code>:调用<code>kvmmap</code>来创建内核页表的映射。</li><li><code>kvminithart</code>:把kernel页表的物理地址写入satp寄存器。从<code>w_satp</code>这行代码后,页表开始启用,地址都变成虚拟地址。</li></ul><p>每个RISC-V CPU会把PTE缓存到<em>Translation Look-aside Buffer(TLB)</em>中,当xv6更改了页表时,必须通知CPU来取消掉当前的TLB,取消当前TLB的函数是sfence.vma(),在kvminithart中被调用</p><h2 id="physical-memory-allocation">3.4 Physical memory allocation</h2><p>xv6在运行时分配或释放页表、用户内存、内核栈、管道缓冲区等各种物理内存的不同用途。xv6中这些内存都分配在<strong>内核数据</strong>的末位和PHYSTOP之间,每次分配4096字节,即4KB空间。</p><p>分配和释放是通过对空闲页链表进行追踪完成的(<code>kernel/kalloc.c:struct kmem</code>),分配空间就是将一个页从链表中移除,释放空间就是将一页增加到链表中。</p><p>kernel的物理空间的分配函数在<code>kernel/kalloc.c</code>中,每个页在链表中的元素是<code>struct run</code>,每个run存储在空闲页本身中。这个空闲页的链表<code>freelist</code>由<code>spin lock</code>保护,包装在<code>struct kmem</code>中。</p><ul><li><code>kinit</code>:初始化所有空闲内存列表,在内核刚启动的时候调用。从<code>kernel end</code>到<code>PHYSTOP</code>之间的所有内存都按页清空,并存放在<code>freelist</code>中。</li><li><code>freerange</code>:将range中的每一个页面都调用一次<code>free()</code>来将其插入到<code>freelist</code>的末尾。</li></ul><h2 id="user-space-address">3.5 User space address</h2><p>每个进程都有一个单独的页表,当内核在进程之间切换的时候,他也会修改对于的页表。<img src="/img/6.S081/Lab-03/figure3-4.png"alt="Figure 3.4: A process’s user address space, with its initial stack." /></p><p>当进程向内核索要更多用户内存的时候,xv6会调用<code>kalloc</code>来分配物理内存。然后会向进程页表中添加新的PTE项,并附带<code>PTE_W</code>、<code>PTE_R</code>、<code>PTR_U</code>和<code>PTE_V</code>标志。</p><p>从这里可以看到页表的几个使用好处:1.不同进程的页表将同样的用户地址映射到不同的内存中,从而每个进程都拥有独自的内存。2.每个进程的虚拟地址都一段从0开始的连续地址,但实际的物理地址并不用为连续的。3.内核将所有进程运行状态都存储在虚拟空间顶部的<code>trampoline</code>页面中(没有设置<code>PTE_U</code>权限),防止用户进程修改其他数据。</p><h2 id="code-exec">3.6 Code : exec</h2><p>exec是一个<code>system call</code>,为以ELF格式定义的文件系统中的可执行文件创建用户空间。</p><p>exec先检查头文件中是否有<code>ELF_MAGIC</code>来判断这个文件是否是一个ELF格式定义的二进制文件,用<code>proc_pagetable</code>来为当前进程创建一个还没有映射的页表,然后用<code>uvmalloc</code>来为每个ELFsegment分配物理空间并在页表中建立映射,然后用<code>loadseg</code>来把ELFsegment加载到物理空间当中。注意<code>uvmalloc</code>分配的物理内存空间可以比文件本身要大。</p><p>接下来exec分配userstack,它仅仅分配一页给stack,通过<code>copyout</code>将传入参数的string放在stack的顶端,然后在ustack的下方分配一个<code>guard page</code></p><p>如果exec检测到错误,将跳转到bad标签,释放新创建的pagetable并返回-1。exec必须确定新的执行能够成功才会释放进程旧的页表(proc_freepagetable(oldpagetable,oldsz)),否则如果system call不成功,就无法向旧的页表返回-1</p>]]></content>
<categories>
<category>6.S081</category>
</categories>
<tags>
<tag>6.S081</tag>
</tags>
</entry>
<entry>
<title>MIT 6.S081 Lab2:System Calls | 系统调用</title>
<link href="/2024/02/25/MIT-6.S081-lab02/"/>
<url>/2024/02/25/MIT-6.S081-lab02/</url>
<content type="html"><![CDATA[<h2 id="前置准备">前置准备</h2><p>主要内容为自己实现系统调用,或者优化已有的系统调用。</p><p>根据<ahref="https://pdos.csail.mit.edu/6.828/2021/schedule.html">课程官网</a>的要求,需要阅读完教材的第二章<spanclass="math inline">\(Operating \space system \spaceorganization\)</span>。并且读懂下列源代码:<code>kernel/proc.h</code>,<code>kernel/defs.h</code>, <code>kernel/entry.S</code>,<code>kernel/main.c</code>, <code>user/initcode.S</code>,<code>user/init.c</code>, 简略了解<code>kernel/proc.c</code>,<code>kernel/exec.c</code>。</p><p>相关笔记参考: <ahref="https://gejxd.github.io/2024/02/25/MIT-6.S081-lab02%20Notes/">lecture2 Notes</a></p><p>将<code>util</code>分支所有内容都上传仓库后,执行下列命令来切换分支<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">git fetch<br>git checkout syscall<br>make clean<br></code></pre></td></tr></table></figure></p><p>此外,新建一个<code>syscall_dev</code>分支来进行实际实验。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git checkout -b syscall_dev<br></code></pre></td></tr></table></figure><p>在<code>syscall_dev</code>中每通过一个作业的测试,提交(gitcommit)你的代码,并将所做的修改合并(git merge)到util中,然后提交(gitpush)到github。</p><figure class="highlight bash"><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></pre></td><td class="code"><pre><code class="hljs bash">git add .<br>git commit -m <span class="hljs-string">"xxxxxxxx"</span><br>git checkout syscall<br>git merge syscall_dev<br>git push github syscall:syscall<br></code></pre></td></tr></table></figure><h2 id="system-call-tracing-moderate">System call tracing(moderate)</h2><h3 id="statement">Statement</h3><blockquote><p>在本作业中,你将添加一项系统调用跟踪功能,这可能会在以后的实验调试中有所帮助。你将创建一个新的跟踪系统调用来控制跟踪。它应该接受一个参数,即一个"掩码",该掩码的位数指定了要跟踪的系统调用。例如,要跟踪 fork系统调用,程序会调用 <code>trace(1<<SYS_fork)</code>,其中SYS_fork 是 kernel/syscall.h 中的系统调用编号。你必须修改 xv6内核,以便在每个系统调用即将返回时,如果掩码中设置了系统调用编号,就打印出一行。该行应包含进程ID、系统调用名称和返回值;无需打印系统调用参数。跟踪系统调用应启用对调用该调用的进程及其随后分叉的子进程的跟踪,但不应影响其他进程。</p></blockquote><figure class="highlight bash"><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></pre></td><td class="code"><pre><code class="hljs bash">$ trace 32 grep hello README<br>3: syscall <span class="hljs-built_in">read</span> -> 1023<br>3: syscall <span class="hljs-built_in">read</span> -> 966<br>3: syscall <span class="hljs-built_in">read</span> -> 70<br>3: syscall <span class="hljs-built_in">read</span> -> 0<br>$<br>$ trace 2147483647 grep hello README<br>4: syscall trace -> 0<br>4: syscall <span class="hljs-built_in">exec</span> -> 3<br>$<br>$ grep hello README<br>$<br>$ trace 2 usertests forkforkfork<br>usertests starting<br><span class="hljs-built_in">test</span> forkforkfork: 407: syscall fork -> 408<br>408: syscall fork -> 409<br>409: syscall fork -> 410<br>410: syscall fork -> 411<br>409: syscall fork -> 412<br>410: syscall fork -> 413<br>409: syscall fork -> 414<br>411: syscall fork -> 415<br>...<br>$ <br></code></pre></td></tr></table></figure><p>在第一个样例中,<code>trace</code> 只在 <code>grep</code> 中追踪<code>read</code> 系统调用。<span class="math inline">\(32\)</span> 是<span class="math inline">\(1 << sys_read\)</span> 的结果。在第二个样例中,<code>trace</code>追踪所有在<code>grep</code>中的系统调用,<spanclass="math inline">\(2147483647\)</span> 是 <spanclass="math inline">\(31\)</span> 位二进制位都置1。 在第三个样例中,进程并没有进行追踪。 在第四个样例中,<code>trace</code>最终所有后续的<code>fork</code>系统调用。</p><p>如果程序正确,应该得到一致的输出(进程号可能有所出入,但其他都应该一致)。</p><h3 id="hints">Hints</h3><ul><li>在 <code>Makefile</code> 中的<code>UPROGS</code> 添加<code>$U/_trace</code>。</li><li>运行 <code>make qemu</code>,你会发现编译器无法编译<code>user/trace.c</code>,因为系统调用的用户空间存根还不存在:在<code>user/user.h</code> 中添加一个系统调用原型,在<code>user/usys.pl</code> 中添加一个存根,在<code>kernel/syscall.h</code> 中添加一个系统调用号。<code>Makefile</code> 会调用 perl 脚本 <code>user/usys.pl</code>,生成<code>user/usys.S</code>,即实际的系统调用存根,它使用 RISC-V ecall指令过渡到内核。解决编译问题后,运行<code>trace 32 grep hello README</code>;由于尚未在内核中实现系统调用,所以会失败),将父进程的跟踪掩码复制到子进程。</li><li>在 <code>kernel/sysproc.c</code> 中添加一个<code>sys_trace()</code>函数,通过在 proc 结构(参见<code>kernel/proc.h</code>)中的一个新变量中记住参数来实现新的系统调用。从用户空间获取系统调用参数的函数在<code>kernel/syscall.c</code> 中,你可以在 <code>kernel/sysproc.c</code>中看到使用这些函数的示例。</li><li>修改 <code>fork()</code>(参见<code>kernel/proc.c</code>),将父进程的跟踪掩码复制到子进程。</li><li>修改 <code>kernel/syscall.c</code> 中的 <code>syscall()</code>函数,以打印跟踪输出。您需要添加一个系统调用名称数组作为索引。</li></ul><h3 id="analysis-solution">Analysis & Solution</h3><p>前文末尾(<ahref="https://gejxd.github.io/2024/02/25/MIT-6.S081-lab02%20Notes/#starting-the-first-process">传送门</a>)已经说过了xv6 进行系统调用的过程,这里就略过了。</p><p>提示<span class="math inline">\(1\)</span>和提示<spanclass="math inline">\(2\)</span>没什么好说的。直接看<code>kernel/sysproc.c</code>文件。可以看到其他系统调用是怎么实现的。下面以<code>exit</code>来分析。</p><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></pre></td><td class="code"><pre><code class="hljs C">uint64 <span class="hljs-title function_">sys_exit</span><span class="hljs-params">(<span class="hljs-type">void</span>)</span> {<br> <span class="hljs-type">int</span> n;<br> <span class="hljs-keyword">if</span> (argint(<span class="hljs-number">0</span>, &n) < <span class="hljs-number">0</span>) <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;<br> <span class="hljs-built_in">exit</span>(n);<br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>; <span class="hljs-comment">// not reached</span><br>}<br></code></pre></td></tr></table></figure><p>可以看到,首先从<code>trapfram->a0</code>寄存器拿出第一个参数(由<code>argint()</code>完成)。然后把更多实现细节放到了<code>exit</code>函数中来实现。可以看出,系统调用基本遵循这个流程:在<code>sys_xxxx</code>中处理参数、互斥锁等失误。如果还有复杂的功能,则放到具体<code>xxxx</code>函数里面实现。</p><p>我们的<code>sys_trace</code>只需要将<code>p->trace_mask</code>置为<code>p->trapframe->a0</code>的值即可。所以有:在<code>kernel/sysproc.c</code>中添加下面代码:<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></pre></td><td class="code"><pre><code class="hljs C"><span class="hljs-type">int</span> <span class="hljs-title function_">sys_trace</span><span class="hljs-params">(<span class="hljs-type">void</span>)</span> {<br> <span class="hljs-type">int</span> mask;<br> <span class="hljs-keyword">if</span> (argint(<span class="hljs-number">0</span>, &mask) < <span class="hljs-number">0</span>) <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;<br> <br> myproc()->trace_mask = mask;<br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></p><p>然后打开<code>kernel/syscall.c</code>,找到<code>syscall()</code>这个函数。所有系统调用都是通过<code>syscall</code>函数来调用的。根据提示,我们只需要将<code>p->trace_mask</code>拿出来,然后看系统调用号这一位是否为<spanclass="math inline">\(1\)</span>,如果为<spanclass="math inline">\(1\)</span>,则输出信息即可。</p><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><code class="hljs C"><span class="hljs-type">static</span> <span class="hljs-type">char</span>* syscall_name[] = <br> {<span class="hljs-string">"#"</span>, <span class="hljs-comment">// to make index start from 1;</span><br> <span class="hljs-string">"fork"</span>, <span class="hljs-string">"exit"</span>, <span class="hljs-string">"wait"</span>, <span class="hljs-string">"pipe"</span>, <span class="hljs-string">"read"</span>, <span class="hljs-string">"kill"</span>,<br> <span class="hljs-string">"exec"</span>, <span class="hljs-string">"fstat"</span>, <span class="hljs-string">"chdir"</span>, <span class="hljs-string">"dup"</span>, <span class="hljs-string">"getpid"</span>, <span class="hljs-string">"sbrk"</span>,<br> <span class="hljs-string">"sleep"</span>, <span class="hljs-string">"uptime"</span>, <span class="hljs-string">"open"</span>, <span class="hljs-string">"write"</span>, <span class="hljs-string">"mknod"</span>, <span class="hljs-string">"unlink"</span>,<br> <span class="hljs-string">"link"</span>, <span class="hljs-string">"mkdir"</span>, <span class="hljs-string">"close"</span>, <span class="hljs-string">"trace"</span>};<br><br><span class="hljs-type">void</span> <span class="hljs-title function_">syscall</span><span class="hljs-params">(<span class="hljs-type">void</span>)</span> {<br> <span class="hljs-type">int</span> num;<br> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">proc</span>* <span class="hljs-title">p</span> =</span> myproc();<br><br> num = p->trapframe->a7;<br><br> <span class="hljs-keyword">if</span> (num > <span class="hljs-number">0</span> && num < NELEM(syscalls) && syscalls[num]) {<br> p->trapframe->a0 = syscal## Submit lab<br><br>新建`time.txt`文件,输入一个整数表明完成所有实验的耗时。<br><br>运行`make grade`, 得分为<span class="hljs-number">100</span>。<br>![](/img/<span class="hljs-number">6.</span>S081/Lab<span class="hljs-number">-01</span>/grade.png)<br><br>运行`make handin`, 根据提示将`API KEY`填入。s -> %d\n<span class="hljs-string">", p->pid, syscall_name[num], p->trapframe->a0);</span><br><span class="hljs-string"> }</span><br><span class="hljs-string"> } else {</span><br><span class="hljs-string"></span><br><span class="hljs-string">为了子进程也能继续追踪,我们看一眼`fork`函数。可以看到,`fork`函数其实就是新创建了一个`struct proc* np`,然后把父进程的所有东西都复制给子进程。同理,我们把`trace_mask`也复制给子进程即可。</span><br><span class="hljs-string"></span><br><span class="hljs-string">```C</span><br><span class="hljs-string">// 在合适位置添加</span><br><span class="hljs-string">np->trace_mask = p->trace_mask;</span><br></code></pre></td></tr></table></figure><p>运行结果: <img src="/img/6.S081/Lab-02/run_trace.png" /> <imgsrc="/img/6.S081/Lab-02/test_trace.png" /></p><h2 id="sysinfo-moderate">sysinfo (moderate)</h2><h3 id="statement-1">Statement</h3><blockquote><p>在本作业中,你将添加一个系统调用<code>sysinfo</code>,用于收集运行系统的信息。系统调用需要一个参数:指向<code>struct sysinfo</code> 的指针(参见<code>kernel/sysinfo.h</code>)。内核应填写该结构体的字段:<code>freemem</code>字段应设置为可用内存的字节数## Submit lab</p></blockquote><p>新建<code>time.txt</code>文件,输入一个整数表明完成所有实验的耗时。</p><p>运行<code>make grade</code>, 得分为100。 <imgsrc="/img/6.S081/Lab-01/grade.png" /></p><p>运行<code>make handin</code>,根据提示将<code>API KEY</code>填入。,<code>nproc</code>字段应设置为状态不是 <code>UNUSED</code>的进程数。我们提供了一个测试程序 <code>sysinfotest</code>;如果打印出<code>sysinfotest: OK</code>,则视为通过。</p><h3 id="hints-1">Hints</h3><ul><li>在 Makefile 的 UPROGS 中添加 $U/_sysinfotest。</li><li>运行 <code>make qemu</code>。将提示 <code>user/sysinfotest.c</code>无法编译。添加系统调 <code>sysinfo</code>,步骤与前面的作业相同。在<code>user/user.h</code> 中声明 <code>sysinfo()</code> 的原型,需##Submit lab</li></ul><p>新建<code>time.txt</code>文件,输入一个整数表明完成所有实验的耗时。</p><p>运行<code>make grade</code>, 得分为100。 <imgsrc="/img/6.S081/Lab-01/grade.png" /></p><p>运行<code>make handin</code>, 根据提示将<code>API KEY</code>填入。解决编译问题后,运行<code>sysinfotest</code>;由于内核中尚未实现系统调用,所以会失败。 -<code>sysinfo</code> 需要将 <code>struct sysinfo</code>复制到用户空间;有关如何使用 <code>copyout()</code>进行复制的示例,请参见 <code>sys_fstat()(kernel/sysfile.c)</code> 和<code>filestat()(kernel/file.c)</code> - 要收集可用内存量,请在<code>kernel/kalloc.c</code> 中添加一个函数。 - 要收集进程数,请在<code>kernel/proc.c</code> 中添加一个函数。</p><h3 id="analysis-solution-1">Analysis & Solution</h3><p>先按照上一个任务的方法加入一个 <code>sysinfo</code> 系统调用. 这里在kernel 文件夹下再写一个 <code>sysinfo.c</code> 来实现这个系统调用. 在<code>Makefile</code> 的 <code>OBJS</code> 加入<code>$K/sysinfo.o \</code>(因为我在sysinfo.c中实现,所以要链接这个文件。如果直接在<code>kernel/proc.c</code>中实现就不用).</p><p>看<code>filestat()</code>函数能得知<code>copyout</code>函数的功能,加上<code>sys_fstat()</code>中虚拟地址是从<code>a0</code>读出来的。所以很容易能写出下面代码。</p><p><code>sysinfo.c</code>: <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></pre></td><td class="code"><pre><code class="hljs C"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"types.h"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"riscv.h"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"defs.h"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"date.h"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"param.h"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"memlayout.h"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"spinlock.h"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"proc.h"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"sysinfo.h"</span></span><br><br><span class="hljs-keyword">extern</span> uint64 <span class="hljs-title function_">freemem</span><span class="hljs-params">()</span>;<br><span class="hljs-keyword">extern</span> uint32 <span class="hljs-title function_">num_proc</span><span class="hljs-params">()</span>;<br><br>uint64 <span class="hljs-title function_">sys_sysinfo</span><span class="hljs-params">(<span class="hljs-type">void</span>)</span> {<br> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">sysinfo</span> <span class="hljs-title">info</span>;</span><br> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">proc</span>* <span class="hljs-title">p</span> =</span> myproc();<br><br> uint64 addr;<br> <span class="hljs-keyword">if</span> (argaddr(<span class="hljs-number">0</span>, &addr) < <span class="hljs-number">0</span>) <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;<br><br> info.freemem = freemem();<br> info.nproc = num_proc();<br><br> <span class="hljs-keyword">if</span> (copyout(p->pagetable, addr, (<span class="hljs-type">char</span>*)&info, <span class="hljs-keyword">sizeof</span>(info)) < <span class="hljs-number">0</span>)<br> <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></p><p>然后是<code>freemem()</code>和<code>num_proc()</code>。</p><p>在<code>kernel/kalloc.c</code>中添加,看一下前面的<code>kalloc()</code>函数,在看一下<code>struct kmem</code>。</p><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></pre></td><td class="code"><pre><code class="hljs C"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">run</span> {</span><br> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">run</span>* <span class="hljs-title">next</span>;</span><br>};<br><br><span class="hljs-class"><span class="hljs-keyword">struct</span> {</span><br> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">spinlock</span> <span class="hljs-title">lock</span>;</span><br> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">run</span>* <span class="hljs-title">freelist</span>;</span><br>} kmem;<br><br><span class="hljs-comment">// Allocate one 4096-byte page of physical memory.</span><br><span class="hljs-comment">// Returns a pointer that the kernel can use.</span><br><span class="hljs-comment">// Returns 0 if the memory cannot be allocated.</span><br><span class="hljs-type">void</span>* <span class="hljs-title function_">kalloc</span><span class="hljs-params">(<span class="hljs-type">void</span>)</span> {<br> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">run</span>* <span class="hljs-title">r</span>;</span><br><br> acquire(&kmem.lock);<br> r = kmem.freelist;<br> <span class="hljs-keyword">if</span> (r) kmem.freelist = r->next;<br> release(&kmem.lock);<br><br> <span class="hljs-keyword">if</span> (r) <span class="hljs-built_in">memset</span>((<span class="hljs-type">char</span>*)r, <span class="hljs-number">5</span>, PGSIZE); <span class="hljs-comment">// fill with junk</span><br> <span class="hljs-keyword">return</span> (<span class="hljs-type">void</span>*)r;<br>}<br></code></pre></td></tr></table></figure><p>其中<code>freelisk</code>是一个链表,存储所有空闲内存页面的地址。所以我们只需要遍历这个链表,每一项都加上<code>PGSIZE</code>即可。</p><p>freemem: <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></pre></td><td class="code"><pre><code class="hljs C">uint64 <span class="hljs-title function_">freemem</span><span class="hljs-params">()</span> {<br> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">run</span>* <span class="hljs-title">r</span>;</span><br> acquire(&kmem.lock);<br> r = kmem.freelist;<br><br> uint64 tot = <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">for</span> (;r;r = r->next) tot += PGSIZE;<br><br> release(&kmem.lock);<br><br> <span class="hljs-keyword">return</span> tot;<br>}<br></code></pre></td></tr></table></figure></p><p>然后翻<code>kernel/proc.c</code>中的<code>procdump</code>函数可以知道,<code>proc</code>数组中存放的是所有进程控制块的地址,所以遍历<code>proc</code>即可。</p><p>在<code>kernel/proc.c</code>中添加: <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></pre></td><td class="code"><pre><code class="hljs C">uint32 <span class="hljs-title function_">num_proc</span><span class="hljs-params">()</span> {<br> uint32 tot = <span class="hljs-number">0</span>;<br> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">proc</span>* <span class="hljs-title">p</span>;</span><br> <span class="hljs-keyword">for</span> (p = proc;p < &proc[NPROC];p ++) {<br> <span class="hljs-keyword">if</span> (p->state != UNUSED) tot ++;<br> }<br> <span class="hljs-keyword">return</span> tot;<br>}<br></code></pre></td></tr></table></figure></p><p>运行结果: <img src="/img/6.S081/Lab-02/run_sysinfo.png" /></p><h2 id="submit-lab">Submit lab</h2><p>新建<code>time.txt</code>文件,输入一个整数表明完成所有实验的耗时。</p><p>运行<code>make grade</code>, 得分为100。 <imgsrc="/img/6.S081/Lab-02/grade.png" /></p><p>运行<code>make handin</code>,根据提示将<code>API KEY</code>填入。</p>]]></content>
<categories>
<category>6.S081</category>
</categories>
<tags>
<tag>6.S081</tag>
</tags>
</entry>
<entry>
<title>MIT 6.S081 第二章笔记 | 操作系统结构</title>
<link href="/2024/02/25/MIT-6.S081-lab02%20Notes/"/>
<url>/2024/02/25/MIT-6.S081-lab02%20Notes/</url>
<content type="html"><![CDATA[<p>操作系统应该实现三个功能:<strong>并发</strong>、<strong>隔离</strong>、<strong>交互</strong>。即能保证多个程序都能分到硬件资源;各个进程之间的内存、指令、数据相互隔离,一个进程崩溃不会影响到其他进程;进程之间能通过受控的接口来进行通信。</p><p>操作系统提供了高级别的抽象,来管理硬件资源。例如,用<strong>文件描述符</strong>来抽象磁盘、内存、管道等资源,用户程序能通过简单的<code>read</code>、<code>write</code>、<code>close</code>来访问所有存储资源,而不用关心是和磁盘、内存、管道、还是标准输入输出交互。</p><h2 id="user-mode-supervisor-mode-machine-mode">2.2 User mode、supervisor mode、 machine mode</h2><p>为了实现进程隔离,<span class="math inline">\(RISC-V\)</span>CPU在硬件上提供3种执行命令的模式:machine mode, supervisor mode, usermode。</p><ol type="1"><li><p><span class="math inline">\(machine \space mode\)</span>:机器模式拥有全权限。CPU以机器模式启动。机器模式大多时候用于配置计算机。xv6执行必要的几行指令后就转为监管模式。</p></li><li><p><span class="math inline">\(supervisor \space mode\)</span>:在监管模式下,CPU可以执行特权指令(<span class="math inline">\(privileged\space instructions\)</span>),比如中断管理、对存储页表的寄存器进行读写操作、执行系统调用。运行在<em>监管模式</em>也称为运行在<em>内核空间</em>(<spanclass="math inline">\(kernel \spacespace\)</span>)。运行在内核空间的程序被称作<em>内核</em>。</p></li><li><p><span class="math inline">\(user \space mode\)</span>:用户模式只能执行用户指令,例如<code>add</code>、<code>jump</code>等简单无害的指令。运行在<em>用户模式</em>也称为运行在<em>用户空间</em>。</p></li></ol><p>运行在用户空间的程序如果执行了特权指令,CPU会转换到特权模式并将该程序强制停止。</p><h2 id="the-kernel-organization">2.3 The kernel organization</h2><p><em>monolithic kernel</em>: 整个操作系统在kernel中,所有systemcall都在supervisor mode下运行。xv6是一个monolithic kernel。</p><p><em>micro kernel</em>: 将必须运行在supervisormode下的操作系统代码压到最小,保证kernel的安全性和简洁,将大部分的操作系统代码执行在usermode下。</p><p>宏内核易于设计,但是系统调用较复杂,并且一旦任意一条特权指令出错,整个操作系统都会崩溃。</p><p>如下图所示,文件系统作为用户级别的进程执行,用户通过进程间通信请求文件系统的服务。这种运行在用户模式的内核模块称作<code>server</code>。微内核更轻便、稳定,但是难于设计和实现。</p><figure><img src="/img/6.S081/Lab-02/micro_kernel.png"alt="Figure: 1.1 A microkernel with a file-system server" /><figcaption aria-hidden="true">Figure: 1.1 A microkernel with afile-system server</figcaption></figure><p>下图列出了 xv6的所有内核文件和其对应的功能。模块间接口定义在<code>kernel/defs.h</code>文件中。<img src="/img/6.S081/Lab-02/kernel_src.png"alt="xv6's kernel source file" /></p><h2 id="process-overview">2.4 Process overview</h2><p>隔离的单元叫作<strong>进程</strong>,一个进程不能破坏或监听另一个进程的内存、CPU、文件描述符,也不能破坏kernel 本身。</p><p>为了加强隔离,内核为每个进程提供了一块私有、独立的内存,称作地址空间(addressspace),这让进程认为自己拥有一个独立的机器,而不用和其他进程共享硬件资源。其他的进程不能访问这块内存。</p><p>操作系统使用<strong>页表(pagetable)</strong>的概念来实现内存独立。页表提供<strong>虚拟地址</strong>(RISC-V操作的地址)到<strong>物理地址</strong>(CPU芯片发送到内存的地址)的映射(或转换)。</p><p>xv6 为每一个进程维护一个独立的页表,如下图所示。地址空间从 <spanclass="math inline">\(0\)</span>号地址开始,首先是指令,然后是全局变量(<strong>栈空间</strong>),之后是进程可以根据需要灵活拓展的<strong>堆空间</strong>(用于<code>malloc</code>)。</p><p>题外话:操作系统中的堆栈和数据结构中的堆栈没有关系。<strong>堆</strong>是指在运行时动态分配的空间,<strong>栈</strong>是在运行前确定的静态空间。</p><figure><img src="/img/6.S081/Lab-02/page_table.png"alt="Figure: 1.2 Layout of a process’s virtual address space" /><figcaption aria-hidden="true">Figure: 1.2 Layout of a process’svirtual address space</figcaption></figure><p>RISC-V使用 <span class="math inline">\(64\)</span> 位指针,但是 xv6只使用低 <span class="math inline">\(38\)</span>位就够了,因此最大地址是 <span class="math inline">\(2^{38} - 1 =0x3fffffffff = MAXVA\)</span>。</p><p>xv6使用<code>struct proc</code>(声明在<code>kernel/proc.h</code>)来维护每个进程的状态。进程最重要的几个信息:1. 页表(<code>p->pagetable</code>). 2.进程栈(<code>p->kstack</code>). 3.进程运行状态(<code>p->state</code>)。</p><p>每个进程中都有线程(<spanclass="math inline">\(thread\)</span>),是执行进程命令的最小单元,可以被暂停和继续。</p><p>每个进程有两个栈:用户栈(user stack)和内核栈(kernelstack)。当进程在userspace中进行时只使用用户堆栈,当进程进入了内核(比如进行了systemcall)使用内核堆栈。</p><p>操作系统给进程提供了两种假象:地址空间给进程提供了独自拥有内存的假象、线程给进程提供了独自拥有 CPU的假象。</p><h2 id="starting-the-first-process">2.5 Starting the first process</h2><p>当RISC-V芯片通电后,它会自动读取 <spanclass="math inline">\(ROM\)</span> 的指令初始化自己,并运行引导程序(在xv6 中为<code>kernel/kernel.ld</code>)将内核加载入内存中。然后在machinemode从_entry(<code>kernel/entry.S</code>)开始运行xv6。bootloader将xv6kernel加载到0x80000000的物理地址中,因为前面的地址中有I/O设备。</p><p><code>start</code>函数中,先以machinemode做了一些配置,然后调用<code>mret</code>指令跳转到supervisor mode,并通过修改 PC 寄存器的值跳转到<code>kernel/main.c</code>。</p><p><code>main</code>先对一些设备和子系统进行初始化,然source/_posts/2024/MIT-6.S081-lab03.md进程将要请求的系统调用号写入<code>p->trapframe->a7</code>,其中<code>p</code>为当前进程的<code>struct proc</code>。并且将参数写入<code>p->trapframe->a0</code>和其他寄存器。之后进程执行<code>ecall</code>指令,并保存进程相关信息(其中就包括<code>trapframe</code>)。然后开始执行<code>syscall(kernel/syscall.c:95)</code></p><p><code>syscall()</code>从<code>trapframe->a7</code>中拿到索引,通过一个函数指针数组<code>syscall[]</code>(定义在<code>kernel/syscall.c</code>中)获取对应系统调用的函数指令。然后将系统调用的返回写入<code>p->trapframe->a0</code>。</p><p>系统调用号( <span class="math inline">\(system \space call \spacenumber\)</span>)定义在<code>kernel/syscall.h</code>中,作为内核找到函数指针的索引。</p><h2 id="system-call-arguments">2.7 System call arguments</h2><p>对应教材第4章第4节。</p><p><code>trap</code>相关的代码将用户寄存器保存在当前进程的<code>trapframe</code>中,内核函数<code>argint</code>、<code>argaddr</code>、<code>argfd</code>从<code>trapframe</code>中的指定寄存器得到数据,并分别按照整数、地址、文件描述符解析。通过<code>argrow</code>能方便的读取第<spanclass="math inline">\(n\)</span>个寄存器的内容(这些函数都定义在<code>kernel\syscall.c</code>中)。</p><p>大部分参数都通过指针来传递,有时用户程序还会请求内核访问特定内存并写入数据。为了防止用户程序传入恶意参数,内核使用<code>fetchstr</code>和<code>copyinstr</code>来实现安全地与用户提供的地址之间传输数据的功能。</p>]]></content>
<categories>
<category>6.S081</category>
</categories>
<tags>
<tag>6.S081</tag>
</tags>
</entry>
<entry>
<title>MIT 6.S081 Lab1 util:Unix utilities | UNIX 实用程序</title>
<link href="/2024/02/25/MIT-6.S081-lab01/"/>
<url>/2024/02/25/MIT-6.S081-lab01/</url>
<content type="html"><![CDATA[<h2 id="前置准备">前置准备</h2><p>主要内容为如何使用已有的系统调用编写用户程序,例如<code>ls</code>、<code>xargs</code>、<code>find</code>等常用命令的实现。</p><p>根据课程官网的要求,需要看完<code>Lecture 1</code>,阅读完教材第一章<spanclass="math inline">\(Operation \space System \spaceinterfaces\)</span>。</p><p>此外,实验手册中的<code>Hints</code>很重要,大多数实验跟着提示一步一步实现都能完成。</p><h2 id="sleep-easy">Sleep (easy)</h2><h3 id="statement">Statement</h3><blockquote><p>为xv6实现 <span class="math inline">\(UNIX\)</span>中的<code>sleep</code>程序。<code>sleep</code>程序应该暂定用户指定的<code>tick</code>数量。<code>tick</code>是xv6内核定义的时间概念,即定时器两次中断之间的间隔时间。你的<code>solution</code>应该存储在<code>user/sleep.c</code>文件下。</p></blockquote><h3 id="hints">Hints</h3><ul><li>在开始实现前,确保你阅读了<code>xv6 book</code>的第一章。</li><li>查看其他xv6中实现的其他用户程序(例如<code>user/echo.c</code>、<code>user/grep.c</code>、<code>user/rm.c</code>),了解如何从终端传递参数到程序中。</li><li>如果用户忘记传递参数,<code>sleep</code>应该显示错误消息并中断。</li><li>终端参数作为字符串传递,你需要用<code>atoi</code>(在<code>user/ulib.c</code>中实现)将其转换为int类型。</li><li>使用<code>sleep</code>系统调用。</li><li>有关实现<code>sleep</code>系统调用(见sys_sleep)的 xv6内核代码,请参见<code>kernel/sysproc.c</code>。有关可从用户程序调用sleep系统调用的 C语言定义,请参见<code>user/user.h</code>;有关从用户代码跳转到内核中<code>sleep</code>的汇编代码,请参见<code>user/usys.S</code>。</li><li>确保<code>main</code>函数中使用<code>exit()</code>来退出程序。</li><li>将<code>sleep.c</code>添加到<code>Makefile</code>的<code>UPROGS</code> 中;完成后,在终端输入 <code>make qemu</code>来编译你的程序,你就可以在 xv6 shell 中运行它了。</li></ul><h3 id="analysis-solution">Analysis & Solution</h3><p>首先按照Hint-1的提示看一眼<code>user/echo.c</code>是什么,可以看到下面的一段代码:</p><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><code class="hljs C"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"kernel/types.h"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"kernel/stat.h"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"user/user.h"</span></span><br><br><span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">(<span class="hljs-type">int</span> argc, <span class="hljs-type">char</span>* argv[])</span> {<br> <span class="hljs-type">int</span> i;<br><br> <span class="hljs-keyword">for</span> (i = <span class="hljs-number">1</span>; i < argc; i++) {<br> write(<span class="hljs-number">1</span>, argv[i], <span class="hljs-built_in">strlen</span>(argv[i]));<br> <span class="hljs-keyword">if</span> (i + <span class="hljs-number">1</span> < argc) {<br> write(<span class="hljs-number">1</span>, <span class="hljs-string">" "</span>, <span class="hljs-number">1</span>);<br> } <span class="hljs-keyword">else</span> {<br> write(<span class="hljs-number">1</span>, <span class="hljs-string">"\n"</span>, <span class="hljs-number">1</span>);<br> }<br> }<br> <span class="hljs-built_in">exit</span>(<span class="hljs-number">0</span>);<br>}<br></code></pre></td></tr></table></figure><p><code>argc</code>存储终端传入的参数数量,<code>argv</code>存储传入的参数的具体内容。</p><p>于是可以写出下面的代码: <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></pre></td><td class="code"><pre><code class="hljs C"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"kernel/types.h"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"kernel/stat.h"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"user/user.h"</span></span><br><br><span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">(<span class="hljs-type">int</span> argc, <span class="hljs-type">char</span>* argv[])</span> {<br> <span class="hljs-keyword">if</span> (argc != <span class="hljs-number">2</span>) {<br> <span class="hljs-built_in">fprintf</span>(<span class="hljs-number">2</span>, <span class="hljs-string">"Usage: sleep <ticks>\n"</span>);<br> <span class="hljs-built_in">exit</span>(<span class="hljs-number">1</span>);<br> }<br> <span class="hljs-type">int</span> time = atoi(argv[<span class="hljs-number">1</span>]);<br> sleep(time);<br><br> <span class="hljs-built_in">exit</span>(<span class="hljs-number">0</span>);<br>}<br></code></pre></td></tr></table></figure></p><p>然后在<code>Makefile</code>中的<code>UPROGS</code>添加一行<code>$U/_sleep\</code>,</p><p><img src="/img/6.S081/Lab-01/UPROGS.png" /></p><p>运行<code>qemu make</code>,编译完成后输入<code>sleep 10</code>,可以看到成功运行。 <img src="/img/6.S081/Lab-01/run_sleep.png" /></p><p>运行<code>./grade-lab-util sleep</code>测试一下。 <imgsrc="/img/6.S081/Lab-01/test_sleep.png" /></p><h2 id="pingpong-easy">PingPong (easy)</h2><h3 id="statement-1">Statement</h3><blockquote><p>编写一个程序,使用 UNIX 系统调用在两个进程之间通过一对管道"ping-pong" 传送一个字节,每个方向发送一次。父进程应向子进程发送一个字节,子进程应打印<code><pid>: received ping</code>(<code><pid></code>是其进程ID),并将一个字节写入父进程的管道,然后退出;父进程应从子进程读取该字节,打印<code><pid>: received pong</code>,然后退出。您的解决方案应放在user/pingpong.c 文件中。</p></blockquote><h3 id="hints-1">Hints</h3><ul><li>使用<code>pipe</code>创建管道</li><li>使用<code>fork</code>创建子进程</li><li>使用<code>read</code>来向管道中的读取端读取数据,<code>write</code>来向管道中的写入端写入数据。</li><li>使用<code>getpid</code>来获取当前进程的PID。</li><li>添加程序到<code>Makefile</code>里的<code>UPROGS</code></li><li>xv6的用户程序可以使用有限的库函数,列出在<code>user/user.h</code>中。源代码(包括其他的系统调用)在<code>user/ulib.c</code>、<code>user/printf.c</code>、<code>user/umalloc.c</code>。</li></ul><h3 id="analysis-solution-1">Analysis & Solution</h3><p>和书上<code>1.3</code>的例子差不多,用大小为2的数组创建管道,其中<code>p[0]</code>是读取端,<code>p[1]</code>是写入端,然后<code>fork</code>分别发送读取。</p><p><code>write</code>和<code>read</code>的第二个参数是一个地址,用char存储的话需要取地址,用char[]就没这么多事了。</p><p>需要注意的是,子进程和父进程的文件描述符是一样的,但并不是引用,在父进程关闭fd不会影响子进程。</p><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><code class="hljs C"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"kernel/types.h"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"kernel/stat.h"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"user/user.h"</span></span><br><br><span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">(<span class="hljs-type">int</span> argc, <span class="hljs-type">char</span>* argv[])</span> {<br> <span class="hljs-type">int</span> p[<span class="hljs-number">2</span>];<br> pipe(p);<br><br> <span class="hljs-type">char</span> msg[<span class="hljs-number">2</span>] = <span class="hljs-string">"@"</span>;<br> <span class="hljs-type">char</span> buf[<span class="hljs-number">2</span>];<br><br> <span class="hljs-keyword">if</span> (fork() != <span class="hljs-number">0</span>) {<br> <span class="hljs-keyword">if</span> (write(p[<span class="hljs-number">1</span>], msg, <span class="hljs-number">1</span>) != <span class="hljs-number">1</span>) {<br> <span class="hljs-built_in">fprintf</span>(<span class="hljs-number">2</span>, <span class="hljs-string">"cannot write a byte to child.\n"</span>);<br> <span class="hljs-built_in">exit</span>(<span class="hljs-number">1</span>);<br> }<br> close(p[<span class="hljs-number">1</span>]);<br><br> <span class="hljs-keyword">if</span> (read(p[<span class="hljs-number">0</span>], buf, <span class="hljs-keyword">sizeof</span>(buf))<br> close(p[<span class="hljs-number">0</span>]);<br> <span class="hljs-built_in">fprintf</span>(<span class="hljs-number">1</span>, <span class="hljs-string">"%d: received pong\n"</span>, getpid());<br> } <span class="hljs-keyword">else</span> {<br> <span class="hljs-keyword">if</span> (read(p[<span class="hljs-number">0</span>], buf, <span class="hljs-keyword">sizeof</span>(buf)) != <span class="hljs-number">1</span>) {<br> <span class="hljs-built_in">fprintf</span>(<span class="hljs-number">2</span>, <span class="hljs-string">"cannot read a byte from parent.\n"</span>);<br> <span class="hljs-built_in">exit</span>(<span class="hljs-number">1</span>);<br> }<br> close(p[<span class="hljs-number">0</span>]);<br> <span class="hljs-built_in">fprintf</span>(<span class="hljs-number">1</span>, <span class="hljs-string">"%d: received ping\n"</span>, getpid());<br><br> <span class="hljs-keyword">if</span> (write(p[<span class="hljs-number">1</span>], msg, <span class="hljs-number">1</span>) != <span class="hljs-number">1</span>) {<br> <span class="hljs-built_in">fprintf</span>(<span class="hljs-number">2</span>, <span class="hljs-string">"cannot write a byte to parent.\n"</span>);<br> <span class="hljs-built_in">exit</span>(<span class="hljs-number">1</span>);<br> }<br> close(p[<span class="hljs-number">1</span>]);<br> }<br>e<br></code></pre></td></tr></table></figure><p>运行结果: <img src="/img/6.S081/Lab-01/run_pingpong.png" /> <imgsrc="/img/6.S081/Lab-01/test_pingpong.png" /></p><h2 id="primes-moderate-hard">Primes (moderate / hard)</h2><h3 id="statement-2">Statement</h3><blockquote><p>通过<code>pipe</code>和<code>fork</code>编写一个并发版本的素数筛。这个想法来自与<spanclass="math inline">\(Doug \spaceMcllroy\)</span>,Unix管道的发明人。本页<ahref="https://swtch.com/~rsc/thread/">下半部分</a>的图片和周围的文字说明了如何操作。你的解决方案应该存放在<code>user/primes.c</code>文件下。</p></blockquote><p>第一个进程将<span class="math inline">\(2\)</span>到<spanclass="math inline">\(35\)</span>输入管道。对于每个质数,你都需要创建一个进程,该进程从左边的邻居管道中读取数据,并通过另一个管道向右边的邻居写入数据。由于xv6的文件描述符是有限的,我们只需要筛出35以内的质数。</p><h3 id="hints-2">Hints</h3><ul><li>及时关闭进程不需要的文件描述符,不然程序会用完有限的文件描述符。</li><li>一旦第一个进程发送了35,它应该直到所有的管道都关闭才退出,包括所有子进程、孙进程等。</li><li>提示: 当写入端的管道被关闭时,<code>read</code>返回<spanclass="math inline">\(0\)</span>。</li><li>最简单的方式是直接向管道写入32位的<code>int</code>数据,而不是用<code>ASCII</code>码来处理。</li><li>确保只在需要创建管道的时候创建管道。</li><li>添加程序到<code>UPROGS</code>。</li></ul><h3 id="analysis-solution-2">Analysis & Solution</h3><p>其实就是通过管道来实现<ahref="https://zh.wikipedia.org/wiki/%E5%9F%83%E6%8B%89%E6%89%98%E6%96%AF%E7%89%B9%E5%B0%BC%E7%AD%9B%E6%B3%95">埃氏筛</a>(wiki打不开<ahref="https://baike.baidu.com/item/%E5%9F%83%E6%8B%89%E6%89%98%E6%96%AF%E7%89%B9%E5%B0%BC%E7%AD%9B%E6%B3%95/374984">点这个</a>)。</p><p>简单解释一下。第一个进程传入<span class="math inline">\(2 \rightarrow35\)</span>给第二个进程,第二个进程输出<spanclass="math inline">\(2\)</span>, 之后把所有没有因子<spanclass="math inline">\(2\)</span>的数都传递给下一个进程。同样,第三个进程输出<spanclass="math inline">\(3\)</span>,然后将所有没有因子<spanclass="math inline">\(3\)</span>的数都传给第<spanclass="math inline">\(4\)</span>个进程...。当管道中没有数的时候,说明所有质数都被筛出来了。整个过程就是参考资料中给的这张图。</p><p><img src="/img/6.S081/Lab-01/seive.png" /></p><p>很显然这是一个递归,还是一个线性的递归,读入完关闭管道就可以了。<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></pre></td><td class="code"><pre><code class="hljs C"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"kernel/types.h"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"kernel/stat.h"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"user/user.h"</span></span><br><br><span class="hljs-type">void</span> <span class="hljs-title function_">primes</span><span class="hljs-params">(<span class="hljs-type">int</span> left[<span class="hljs-number">2</span>], <span class="hljs-type">int</span> base)</span> {<br> <span class="hljs-built_in">fprintf</span>(<span class="hljs-number">1</span>, <span class="hljs-string">"prime %d\n"</span>, base);<br><br> <span class="hljs-type">int</span> right[<span class="hljs-number">2</span>];<br> pipe(right);<br><br> <span class="hljs-comment">// 将剔除后的数写入缓冲区</span><br> <span class="hljs-type">int</span> cur, first = <span class="hljs-number">-1</span>;<br> <span class="hljs-keyword">while</span> (read(left[<span class="hljs-number">0</span>], &cur, <span class="hljs-keyword">sizeof</span>(cur)) == <span class="hljs-keyword">sizeof</span>(cur)) {<br> <span class="hljs-keyword">if</span> (cur % base == <span class="hljs-number">0</span>) <span class="hljs-keyword">continue</span>;<br> <span class="hljs-keyword">if</span> (first == <span class="hljs-number">-1</span>) first = cur;<br> <span class="hljs-keyword">if</span> (write(right[<span class="hljs-number">1</span>], &cur, <span class="hljs-keyword">sizeof</span>(cur)) != <span class="hljs-keyword">sizeof</span>(cur)) {<br> <span class="hljs-built_in">fprintf</span>(<span class="hljs-number">2</span>, <span class="hljs-string">"pid %d: cannot wirite %d bytes to right.\n"</span>, getpid(), <span class="hljs-keyword">sizeof</span>(cur));<br> <span class="hljs-built_in">exit</span>(<span class="hljs-number">1</span>);<br> }<br> }<br> close(left[<span class="hljs-number">0</span>]);<br><br> <span class="hljs-keyword">if</span> (first == <span class="hljs-number">-1</span>) {<br> <span class="hljs-built_in">exit</span>(<span class="hljs-number">0</span>);<br> }<br><br> <span class="hljs-keyword">if</span> (fork() != <span class="hljs-number">0</span>) {<br> close(right[<span class="hljs-number">1</span>]);<br> close(right[<span class="hljs-number">0</span>]);<br> <span class="hljs-comment">// 第i个子进程等待第i+1个子进程结束</span><br> wait((<span class="hljs-type">int</span>*)<span class="hljs-number">0</span>);<br> } <span class="hljs-keyword">else</span> {<br> close(right[<span class="hljs-number">1</span>]);<br> primes(right, first);<br> }<br><br> <span class="hljs-keyword">return</span> ;<br>}<br><br><span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">(<span class="hljs-type">int</span> argc, <span class="hljs-type">char</span>* argv[])</span> {<br> <span class="hljs-type">int</span> p[<span class="hljs-number">2</span>];<br> pipe(p);<br><br> <span class="hljs-comment">// 主进程 所有数都写入管道</span><br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">2</span>; i <= <span class="hljs-number">35</span>; i++) {<br> <span class="hljs-keyword">if</span> (write(p[<span class="hljs-number">1</span>], &i, <span class="hljs-keyword">sizeof</span>(<span class="hljs-type">int</span>)) != <span class="hljs-keyword">sizeof</span>(<span class="hljs-type">int</span>)) {<br> <span class="hljs-built_in">fprintf</span>(<span class="hljs-number">2</span>, <span class="hljs-string">"cannot write %d bytes.\n"</span>, <span class="hljs-keyword">sizeof</span>(<span class="hljs-type">int</span>));<br> }<br> }<br> <br> <span class="hljs-keyword">if</span> (fork() != <span class="hljs-number">0</span>) {<br> close(p[<span class="hljs-number">1</span>]);<br> close(p[<span class="hljs-number">0</span>]);<br> <span class="hljs-comment">// 主进程等待第一个子进程结束</span><br> wait((<span class="hljs-type">int</span>*)<span class="hljs-number">0</span>);<br> } <span class="hljs-keyword">else</span> {<br> close(p[<span class="hljs-number">1</span>]);<br> primes(p, <span class="hljs-number">2</span>);<br> }<br><br> <span class="hljs-built_in">exit</span>(<span class="hljs-number">0</span>);<br>}<br></code></pre></td></tr></table></figure></p><p>运行结果: <img src="/img/6.S081/Lab-01/run_primes.png" /> <imgsrc="/img/6.S081/Lab-01/test_primes.png" /></p><h2 id="find-moderate">find (moderate)</h2><h3 id="statement-3">Statement</h3><blockquote><p>写一个简单版本的UNIXfind程序:在文件树中找到有特定文件名的文件。你的解决方案应该放在<code>user/find.c</code>下。</p></blockquote><h3 id="hints-3">Hints</h3><ul><li>查看<code>user/ls.c</code>学习如何读取目录。</li><li>通过递归来允许查找子目录。</li><li>不要递归进<code>'.'</code>和<code>'..'</code>中。</li><li>对文件系统的更改会在运行qemu是持续存在,要恢复感觉的文件系统,请运行<code>make clean</code>,然后再运行<code>make qemu</code>。</li><li>你将会用到C strings。请参阅 <span class="math inline">\(K \&R\)</span>第5.5节。</li><li>注意不能像 Python 那样使用 <code>==</code> 比较字符串。请使用<code>strcmp()</code> 代替。</li><li>将程序添加到 Makefile 的 <code>UPROGS</code> 中。</li></ul><h3 id="analysis-solution-3">Analysis & Solution</h3><p>提示中提到了<code>user/ls.c</code>文件,我们先看一下<code>ls</code>命令的源码。</p><p>一开始是一个<code>fmtname</code>的函数,传入一个字符串,然后限制字符串长度为<code>DIRSIZ</code>。</p><p>之后就是<code>ls</code>函数的主体。先看一下<code>dirent</code>和<code>stat</code>两个结构体是什么。</p><p>在<code>kernel/fs.h</code>的末尾有这么两段代码 <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></pre></td><td class="code"><pre><code class="hljs C"><span class="hljs-comment">// Directory is a file containing a sequence of dirent structures.</span><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> DIRSIZ 14</span><br><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">dirent</span> {</span><br> ushort inum;<br> <span class="hljs-type">char</span> name[DIRSIZ];<br>};<br></code></pre></td></tr></table></figure>注释中明确说了"目录是一个包括一系列<code>dirent</code>结构体的文件'"。并且<code>name</code>字段的大小是<code>DIRSIZ</code>,这也解释了为什么<code>fmtname</code>要限制字符串长度。</p><p>在<code>kernel/stat.h</code>中可以看到下面这段代码 <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></pre></td><td class="code"><pre><code class="hljs C"><span class="hljs-meta">#<span class="hljs-keyword">define</span> T_DIR 1 <span class="hljs-comment">// Directory</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> T_FILE 2 <span class="hljs-comment">// File</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> T_DEVICE 3 <span class="hljs-comment">// Device</span></span><br><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">stat</span> {</span><br> <span class="hljs-type">int</span> dev; <span class="hljs-comment">// File system's disk device</span><br> uint ino; <span class="hljs-comment">// Inode number</span><br> <span class="hljs-type">short</span> type; <span class="hljs-comment">// Type of file</span><br> <span class="hljs-type">short</span> nlink; <span class="hljs-comment">// Number of links to file</span><br> uint64 size; <span class="hljs-comment">// Size of file in bytes</span><br>};<br></code></pre></td></tr></table></figure>结合<code>user/ls.c</code>中两个<code>if</code>中的处理。 <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></pre></td><td class="code"><pre><code class="hljs C"><span class="hljs-keyword">if</span> ((fd = open(path, <span class="hljs-number">0</span>)) < <span class="hljs-number">0</span>) {<br> <span class="hljs-built_in">fprintf</span>(<span class="hljs-number">2</span>, <span class="hljs-string">"ls: cannot open %s\n"</span>, path);<br> <span class="hljs-keyword">return</span>;<br> }<br><br> <span class="hljs-keyword">if</span> (fstat(fd, &st) < <span class="hljs-number">0</span>) {<br> <span class="hljs-built_in">fprintf</span>(<span class="hljs-number">2</span>, <span class="hljs-string">"ls: cannot stat %s\n"</span>, path);<br> close(fd);<br> <span class="hljs-keyword">return</span>;<br> }<br></code></pre></td></tr></table></figure>至此文件系统就能看出个大概了。在XV6中,一切都是以文件的形式来存储的,无论是目录、文本、设备等等所有东西,都被内核视为文件,只不过按不同的格式读取,或者以<code>stat</code>等结构体来标明他是什么。</p><p>正因为一切都是文件,所以我们可以用<code>open</code>系统调用来打开任意对象(例如上面的<code>open(path, 0)</code>),并返回一个文件描述符,通过这个文件描述符,我们可以按照不同的结构来读出对象里的内容(可以从<code>path</code>中读出<code>dirent</code>,即目录中的文件名、路径名)。</p><p>对于每个读出的<code>dirent</code>对象,可以用<code>fstat</code>系统调用来获取他的信息,存储在一个<code>stat</code>类型的结构体中。例如,<code>stat.type</code>中指定了当前<code>dirent</code>是文件、目录、还是设备。</p><p>于是这道题的思路就很清晰了,不停从用户指定的路径中读出<code>dirent</code>,如果当前<code>dirent</code>是文件,则判断<code>dirent.name</code>是否和要查找的文件相同。如果当前<code>dirent</code>是目录,则递归遍历这个子目录。</p><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></pre></td><td class="code"><pre><code class="hljs C"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"kernel/types.h"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"kernel/stat.h"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"user/user.h"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"kernel/fs.h"</span></span><br><br><span class="hljs-type">void</span> <span class="hljs-title function_">find</span><span class="hljs-params">(<span class="hljs-type">char</span>* path, <span class="hljs-type">char</span>* filename)</span> {<br> <span class="hljs-type">int</span> fd;<br> <span class="hljs-keyword">if</span> ((fd = open(path, <span class="hljs-number">0</span>)) < <span class="hljs-number">0</span>) {<br> <span class="hljs-built_in">fprintf</span>(<span class="hljs-number">2</span>, <span class="hljs-string">"find: cannot open %s\n"</span>, path);<br> <span class="hljs-keyword">return</span>;<br> }<br><br> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">stat</span> <span class="hljs-title">st</span>;</span><br> <span class="hljs-keyword">if</span> (fstat(fd, &st) < <span class="hljs-number">0</span>) {<br> <span class="hljs-built_in">fprintf</span>(<span class="hljs-number">2</span>, <span class="hljs-string">"find: cannot stat %s\n"</span>, path);<br> close(fd);<br> <span class="hljs-keyword">return</span>;<br> }<br><br> <span class="hljs-keyword">if</span> (st.type != T_DIR) {<br> <span class="hljs-built_in">fprintf</span>(<span class="hljs-number">2</span>, <span class="hljs-string">"find: %s should be a dir, but found a file.\n"</span>, path);<br> <span class="hljs-built_in">exit</span>(<span class="hljs-number">1</span>);<br> }<br><br> <span class="hljs-type">char</span> buf[<span class="hljs-number">512</span>];<br> <br> <span class="hljs-comment">// strlen(path) + 1 表示path的长度 + '\0'</span><br> <span class="hljs-comment">// DIRSIZ 是文件名最大长度, 见kernel/fs.h 54行</span><br> <span class="hljs-comment">// 最后还要在末位加上`/`,e.g. code/user -> code/user/</span><br> <span class="hljs-keyword">if</span> (<span class="hljs-built_in">strlen</span>(path) + <span class="hljs-number">1</span> + DIRSIZ + <span class="hljs-number">1</span> > <span class="hljs-keyword">sizeof</span> buf) {<br> <span class="hljs-built_in">fprintf</span>(<span class="hljs-number">2</span>, <span class="hljs-string">"find: path too long.\n"</span>);<br> <span class="hljs-built_in">exit</span>(<span class="hljs-number">1</span>);<br> }<br><br> <span class="hljs-built_in">strcpy</span>(buf, path);<br> <span class="hljs-type">char</span> *p = buf + <span class="hljs-built_in">strlen</span>(buf);<br> *p ++ = <span class="hljs-string">'/'</span>;<br> <span class="hljs-comment">// 运算优先级: *p ---> p ++. 即p指向'/'的后一个位置</span><br><br> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">dirent</span> <span class="hljs-title">de</span>;</span><br> <span class="hljs-keyword">while</span> (read(fd, &de, <span class="hljs-keyword">sizeof</span>(de)) == <span class="hljs-keyword">sizeof</span>(de)) {<br> <span class="hljs-keyword">if</span> (de.inum == <span class="hljs-number">0</span>) <span class="hljs-keyword">continue</span>;<br> <span class="hljs-comment">// 将de.name复制到p后面,最大长度为DIRSIZ</span><br> memmove(p, de.name, DIRSIZ);<br> p[DIRSIZ] = <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">if</span> (stat(buf, &st) < <span class="hljs-number">0</span>) {<br> <span class="hljs-built_in">fprintf</span>(<span class="hljs-number">2</span>, <span class="hljs-string">"find: cannot stat %s\n"</span>, buf);<br> <span class="hljs-keyword">continue</span>;<br> }<br> <span class="hljs-keyword">switch</span> (st.type) {<br> <span class="hljs-keyword">case</span> T_FILE:<br> <span class="hljs-keyword">if</span> (<span class="hljs-built_in">strcmp</span>(de.name, filename) == <span class="hljs-number">0</span>) <span class="hljs-built_in">fprintf</span>(<span class="hljs-number">1</span>, <span class="hljs-string">"%s\n"</span>, buf);<br> <span class="hljs-keyword">break</span>;<br> <span class="hljs-keyword">case</span> T_DIR:<br> <span class="hljs-keyword">if</span> (<span class="hljs-built_in">strcmp</span>(de.name, <span class="hljs-string">"."</span>) == <span class="hljs-number">0</span>) <span class="hljs-keyword">continue</span>;<br> <span class="hljs-keyword">if</span> (<span class="hljs-built_in">strcmp</span>(de.name, <span class="hljs-string">".."</span>) == <span class="hljs-number">0</span>) <span class="hljs-keyword">continue</span>;<br> find(buf, filename);<br> }<br> }<br>}<br><br><span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">(<span class="hljs-type">int</span> argc, <span class="hljs-type">char</span>* argv[])</span> {<br> <span class="hljs-keyword">if</span> (argc < <span class="hljs-number">2</span>) {<br> <span class="hljs-built_in">fprintf</span>(<span class="hljs-number">2</span>, <span class="hljs-string">"Usage: find [path] <filename>\n"</span>);<br> <span class="hljs-built_in">exit</span>(<span class="hljs-number">1</span>);<br> }<br><br> <span class="hljs-keyword">if</span> (argc == <span class="hljs-number">2</span>) {<br> find(<span class="hljs-string">"."</span>, argv[<span class="hljs-number">1</span>]);<br> } <span class="hljs-keyword">else</span> {<br> find(argv[<span class="hljs-number">1</span>], argv[<span class="hljs-number">2</span>]);<br> }<br><br> <span class="hljs-built_in">exit</span>(<span class="hljs-number">0</span>);<br>}<br></code></pre></td></tr></table></figure><p>运行结果: <img src="/img/6.S081/Lab-01/run_find.png" /> <imgsrc="/img/6.S081/Lab-01/test_find.png" /></p><h2 id="xargs-moderate">xargs (moderate)</h2><h3 id="statement-4">Statement</h3><blockquote><p>编写一个简单版本的 <code>UNIX xargs</code>程序:从标准输入中读取行,并为每一行运行一个命令,同时将该行作为参数提供给命令。你的解决方案应放在<code>user/xargs.c</code> 文件中。</p></blockquote><p>下面的示例说明了 <code>xarg</code> 的行为: <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ <span class="hljs-built_in">echo</span> hello too | xargs <span class="hljs-built_in">echo</span> <span class="hljs-built_in">bye</span><br><span class="hljs-built_in">bye</span> hello too<br>$<br></code></pre></td></tr></table></figure></p><p>请注意,这里的命令是 “echo bye”,而附加参数是 “hello too”,因此命令是“echo bye hello too”,输出结果是 "bye hello too”</p><p>请注意,UNIX 上的 <code>xargs</code>会进行优化,一次向命令提供多个参数。我们不希望你进行这种优化。要使 UNIX上的 <code>xargs</code> 按我们希望的方式运行,请在运行时将 -n 选项设为1。例如</p><figure class="highlight bash"><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></pre></td><td class="code"><pre><code class="hljs bash">$ <span class="hljs-built_in">echo</span> <span class="hljs-string">"1\n2"</span> | xargs -n 1 <span class="hljs-built_in">echo</span> line<br>line 1<br>line 2<br>$<br></code></pre></td></tr></table></figure><h3 id="hints-4">Hints</h3><ul><li>使用 fork 和 exec 在每一行输入中调用命令。在父进程中使用 wait等待子进程完成命 令。</li><li>要读取单行输入内容,每次读取一个字符(<code>read</code>),直到出现换行符('')。</li><li><code>kernel/param.h</code> 声明了 <code>MAXARG</code>,这在需要声明<code>argv</code> 数组时可能有用。</li><li>将程序添加到 Makefile 的 UPROGS 中。</li><li>对文件系统的更改会在运行 qemu时持续存在;要获得一个干净的文件系统,请运行<code>make clean</code>,然后再运行 <code>make qemu</code>。</li></ul><h3 id="analysis-solution-4">Analysis & Solution</h3><p><code>xargs</code>的功能是给定一个命令,例如上面的<code>echo line</code>,然后从<strong>标准输入流</strong>中读出许多行,把每一行的输入作为参数附加到给定的命令中。</p><p>比如 <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ find . b | xargs <span class="hljs-built_in">echo</span> hello<br>hello ./b<br>hello ./a/b<br></code></pre></td></tr></table></figure>对每一行读入都<code>fork</code>然后<code>exec</code>,模拟这个过程就行(指针指晕了)。<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></pre></td><td class="code"><pre><code class="hljs C"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"kernel/types.h"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"kernel/stat.h"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"user/user.h"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"kernel/param.h"</span></span><br><br><span class="hljs-type">int</span> <span class="hljs-title function_">getline</span><span class="hljs-params">(<span class="hljs-type">char</span>* buf, <span class="hljs-type">const</span> <span class="hljs-type">int</span> max)</span> {<br> <span class="hljs-type">int</span> cnt = <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">while</span> (read(<span class="hljs-number">0</span>, buf, <span class="hljs-keyword">sizeof</span>(<span class="hljs-type">char</span>)) > <span class="hljs-number">0</span>) {<br> <span class="hljs-keyword">if</span> (++cnt > max) {<br> <span class="hljs-built_in">fprintf</span>(<span class="hljs-number">2</span>, <span class="hljs-string">"xargs: input lines are too long.\n"</span>);<br> <span class="hljs-built_in">exit</span>(<span class="hljs-number">1</span>);<br> }<br> <span class="hljs-keyword">if</span> (*buf++ == <span class="hljs-string">'\n'</span>) <span class="hljs-keyword">break</span>;<br> }<br> *(buf - <span class="hljs-number">1</span>) = <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">return</span> cnt;<br>}<br><br><span class="hljs-type">int</span> <span class="hljs-title function_">parse</span><span class="hljs-params">(<span class="hljs-type">char</span>** args, <span class="hljs-type">char</span>* buf, <span class="hljs-type">const</span> <span class="hljs-type">int</span> max)</span> {<br> <span class="hljs-type">int</span> cnt = <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">while</span> (*buf) {<br> <span class="hljs-keyword">if</span> (cnt >= max) {<br> <span class="hljs-built_in">fprintf</span>(<span class="hljs-number">2</span>, <span class="hljs-string">"xargs: args are too many.\n"</span>);<br> <span class="hljs-built_in">exit</span>(<span class="hljs-number">1</span>);<br> }<br> <span class="hljs-keyword">while</span> (*buf && *buf == <span class="hljs-string">' '</span>) buf ++;<br> <span class="hljs-keyword">if</span> (*buf == <span class="hljs-number">0</span>) <span class="hljs-keyword">break</span>;<br> args[cnt ++] = buf;<br> <span class="hljs-keyword">while</span> (*buf && *buf != <span class="hljs-string">' '</span>) buf ++;<br> <span class="hljs-keyword">if</span> (*buf) *buf++ = <span class="hljs-string">'\0'</span>;<br> }<br> <span class="hljs-keyword">return</span> cnt;<br>}<br><br><span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">(<span class="hljs-type">int</span> argc, <span class="hljs-type">char</span>* argv[])</span> {<br> <span class="hljs-keyword">if</span> (argc < <span class="hljs-number">2</span>) {<br> <span class="hljs-built_in">fprintf</span>(<span class="hljs-number">2</span>, <span class="hljs-string">"Usage: xargs <command> [arguments]\n"</span>);<br> <span class="hljs-built_in">exit</span>(<span class="hljs-number">1</span>);<br> }<br><br> <span class="hljs-type">char</span>* args[MAXARG] = {<span class="hljs-number">0</span>};<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">1</span>; i < argc; i++) {<br> args[i - <span class="hljs-number">1</span>] = argv[i];<br> }<br><br> <span class="hljs-type">char</span> buf[<span class="hljs-number">512</span>] = {<span class="hljs-number">0</span>};<br> <span class="hljs-keyword">while</span> (getline(buf, <span class="hljs-keyword">sizeof</span> buf)) {<br> parse(args + argc - <span class="hljs-number">1</span>, buf, MAXARG - argc + <span class="hljs-number">1</span>);<br> <span class="hljs-type">int</span> pid = fork();<br> <span class="hljs-keyword">if</span> (pid > <span class="hljs-number">0</span>) {<br> wait((<span class="hljs-type">int</span>*)<span class="hljs-number">0</span>);<br> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (pid == <span class="hljs-number">0</span>) {<br> <span class="hljs-type">char</span> cmd[<span class="hljs-number">128</span>];<br> <span class="hljs-built_in">strcpy</span>(cmd, args[<span class="hljs-number">0</span>]);<br> exec(cmd, args);<br> } <span class="hljs-keyword">else</span> {<br> <span class="hljs-built_in">fprintf</span>(<span class="hljs-number">2</span>, <span class="hljs-string">"xargs: fork error.\n"</span>);<br> <span class="hljs-built_in">exit</span>(<span class="hljs-number">1</span>);<br> }<br> }<br><br> <span class="hljs-built_in">exit</span>(<span class="hljs-number">0</span>);<br>}<br></code></pre></td></tr></table></figure></p><p>运行结果: <img src="/img/6.S081/Lab-01/run_xargs.png" /> <imgsrc="/img/6.S081/Lab-01/test_xargs.png" /></p><h2 id="submit-lab">Submit lab</h2><p>新建<code>time.txt</code>文件,输入一个整数表明完成所有实验的耗时。</p><p>运行<code>make grade</code>, 得分为100。 <imgsrc="/img/6.S081/Lab-01/grade.png" /></p><p>运行<code>make handin</code>,根据提示将<code>API KEY</code>填入。</p>]]></content>
<categories>
<category>6.S081</category>
</categories>
<tags>
<tag>6.S081</tag>
</tags>
</entry>
<entry>
<title>MIT 6.S081 lab0:配置xv6环境+vscode调试</title>
<link href="/2024/02/08/MIT-6.S081-lab0-%E9%85%8D%E7%8E%AF%E5%A2%83/"/>
<url>/2024/02/08/MIT-6.S081-lab0-%E9%85%8D%E7%8E%AF%E5%A2%83/</url>
<content type="html"><![CDATA[<h2 id="前言">前言</h2><p>系统为Archlinux物理机,环境如下:</p><p><img src="/img/6.S081/neofetch.png" /></p><p>使用的xv6版本为2021版,运行环境为vscode + clangd + gbd。</p><h2 id="安装依赖">安装依赖</h2><p>参考:https://pdos.csail.mit.edu/6.828/2021/tools.html</p><p><strong>首先安装转义riscv64的包:</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">sudo</span> pacman -S riscv64-linux-gnu-binutils riscv64-linux-gnu-gcc riscv64-linux-gnu-gdb<br></code></pre></td></tr></table></figure><p><strong>测试一下环境</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">riscv64-linux-gnu-gcc --version<br>qemu-system-riscv64 --version<br></code></pre></td></tr></table></figure><p>如果出现下图就说明成功了:</p><p><img src="/img/6.S081/riscv64.png" /></p><p><strong>安装运行xv6的模拟器:</strong></p><p>官网上的qemu-arch-extra已经被替换为了qemu-arch-extra-git。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">sudo</span> pacman -S qemu-arch-extra-git<br></code></pre></td></tr></table></figure><p>到这需要的环境就装完了<del>(不会装vscode建议退课)</del>。</p><h2 id="编译运行xv6">编译运行xv6</h2><p>参考:https://pdos.csail.mit.edu/6.828/2021/labs/util.html</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">git <span class="hljs-built_in">clone</span> git://g.csail.mit.edu/xv6-labs-2021<br><span class="hljs-built_in">cd</span> xv6-labs-2021<br></code></pre></td></tr></table></figure><p>课程里提到实验用的xv6和实际发布的不太一样,所以别下载github上的,用课程官网的这个。如果要其他年份的就把末尾的<code>xv6-labs-2021</code>改为其他年份就行。</p><p>可以发现master分支里啥也没有,我们先切换到lab1用到的<code>util</code>分支:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git checkout util<br></code></pre></td></tr></table></figure><p>这时候<code>ls</code>一下就会出现xv6的源码了:</p><p><img src="/img/6.S081/utils.png" /></p><p>然后用<code>qemu</code>编译一下看看</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">make qemu<br></code></pre></td></tr></table></figure><p><img src="/img/6.S081/qemu.png" /></p><p>最后出现这几行就说明编译成功了,按<code>ctrl + a, x</code>退出模拟环境。</p><p><img src="/img/6.S081/boot.png" /></p><p>这里会遇到几个坑:</p><h3id="编译显示infinite-recursion-detected">编译显示<code>infinite recursion detected</code></h3><p><img src="/img/6.S081/error.png" /></p><p>解决方案见https://github.com/mit-pdos/xv6-riscv/pull/126。</p><p>由于实现的是操作系统,需要很高的代码可靠性,所以MIT在<code>Makefile</code>里设置了把所有警告都视为错误。在<code>user/sh.c</code>中<code>runcmd</code>前添加一行<code>__attribute__((noreturn))</code>即可。</p><p><img src="/img/6.S081/runcmd.png" /></p><h3 id="编译在最后一步卡住">编译在最后一步卡住</h3><p>如果用的是2020版的<code>xv6</code>,由于老版本的<code>qemu</code>和新版本的不兼容,可以使用<code>downgrage</code>来降级<code>qemu</code>.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">sudo</span> downgrade qemu-arch-extra<br></code></pre></td></tr></table></figure><p>选择<code>5.2.0</code>或以前的版本即可。</p><p>也可以用源码编译安装老版本的<code>qemu</code>,参考:https://www.xujintong.com/2023/12/24/187/</p><p><strong>PS:经过亲身试验,2021版和2020版在实验内容上一模一样,所以建议用2021版,这样就不用降级<code>qemu</code>。</strong></p><h2 id="配置vscode和clangd">配置VScode和clangd</h2><p>配中文、主题什么的就不说了。</p><p>需要安装的插件有两个:C/C++(用来配置GDB调试)、clangd(代码补全)。</p><p><img src="/img/6.S081/C-C++.png" /></p><p><img src="/img/6.S081/clangd.png" /></p><p>可以发现<code>clangd</code>找不到标准库和<code>xv6</code>的库,需要用<code>compile_commands.json</code>文件来指明,可以用<code>bear</code>包来自动生成。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">sudo</span> pacman -S bear<br>make clean <span class="hljs-comment"># 清空上一次编译的内容</span><br>bear -- make qemu<br></code></pre></td></tr></table></figure><p>然后<code>compile_commands.json</code>文件里就指明了编译所需头文件的路径:</p><p><img src="/img/6.S081/compile_commands.png" /></p><h2 id="配置代码自动调整格式可选">配置代码自动调整格式(可选)</h2><p><code>xv6</code>的代码风格也太古老了,看的特别不顺眼,所以用<code>llvm</code>的代码风格魔改了一下。</p><p>首先按<code>ctrl + ,</code>,进入<code>vscode</code>的设置,搜索<code>format on save</code>,打上钩,这样在按<code>ctrl + s</code>保存代码的时候就可以自动格式化代码。</p><p>然后在<code>xv6</code>的根目录新建文件<code>.clang-format</code>,借用一下这个老哥的文件:https://github.com/kehanXue/google-style-clang-format/blob/master/.clang-format。把里面的<code>.clang-format</code>复制到我们的文件里面就行,<code>tab-size</code>什么的不习惯2也可以改为4。</p><p>需要注意的是,不要配置自动为头文件排序,不然声明顺序不对会提示函数未声明。在<code>.clangd-format</code>里面设置<code>SortIncludes: false</code>。</p><h2 id="使用vscode-gdb调试">使用VScode + gdb调试</h2><p>用<code>terminal</code>调试效率太低了(也可能是我太菜了),所以用<code>vscode</code>配置<code>launch.json</code>来用图形化界面。</p><p>详细解释参考:https://www.cnblogs.com/KatyuMarisaBlog/p/13727565.html,这里就不说细节了。</p><p>在<code>xv6</code>根目录新建目录<code>.vscode</code>,里面新建文件<code>launch.json</code>。把下面这一段东西复制进去:</p><figure class="highlight json"><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><code class="hljs json"><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"version"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0.2.0"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"configurations"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"debug xv6"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"cppdbg"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"request"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"launch"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"program"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"${workspaceFolder}/kernel/kernel"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"args"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"stopAtEntry"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"cwd"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"${workspaceFolder}"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"miDebuggerServerAddress"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"localhost:26000"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"miDebuggerPath"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"/usr/bin/riscv64-linux-gnu-gdb"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"environment"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"externalConsole"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"MIMode"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"gdb"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"setupCommands"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"description"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"pretty printing"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"text"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"-enable-pretty-printing"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"ignoreFailures"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"logging"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span><br> <span class="hljs-comment">// "engineLogging": true,</span><br> <span class="hljs-comment">// "programOutput": true,</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">]</span><br><span class="hljs-punctuation">}</span><br></code></pre></td></tr></table></figure><p>注意<code>midDebuggerPath</code>这一行,如果你用的是<code>riscv64-linux-gnu-gdb</code>就填<code>riscv64-linux-gnu-gdb</code>,如果是<code>riscv64-unknown-elf-gcc</code>就填<code>riscv64-unknown-elf-gcc</code>,其他的同理。</p><p>之后运行</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">make qemu-gdb<br></code></pre></td></tr></table></figure><p>来开启<code>qemu</code>的<code>gdb</code>接口,可以看到用的接口是<code>26000</code>。</p><p><img src="/img/6.S081/qemu-gdb.png" /></p><p>在<code>xv6</code>根目录会自动新增一个<code>gdbinit</code>文件,把文件里的<code>target remote 127.0.0.1:26000</code>行删除。</p><p><img src="/img/6.S081/gdbinit.png" /></p><p>每一次执行<code>make clean</code>时都要把这一行删除,目前没找到一劳永逸的办法。</p><p>然后在需要调试的文件里添加断点,按下<code>F5</code>,<code>gbd</code>正确停止到<code>kernel</code>的<code>main</code>函数里。</p><p><img src="/img/6.S081/gdb_main.png" /></p><p>在需要调试的代码前加断点,这里用第一个实验<code>sleep.c</code>为例:</p><p><img src="/img/6.S081/sleep_break.png" /></p><p><img src="/img/6.S081/break_point.png" /></p><p>可以看到断点是灰红色+空心的,说明<code>vscode</code>并没有正确切换符号表,我们打开调试控制台,输入<code>-exec file /user/_sleep</code>,然后就发现断点变红+实心了。</p><p><img src="/img/6.S081/exec.png" /></p><p><img src="/img/6.S081/bread_point_red.png" /></p><p>在<code>xv6</code>终端中输入<code>sleep 10</code>来运行程序,点击上方的继续按钮,<code>gdb</code>将会运行到断点处停下,然后就和调试其他程序一样用就行。</p><p><img src="/img/6.S081/sleep_10.png" /></p><p><img src="/img/6.S081/run.png" /></p><h2 id="关于提交lab">关于提交lab</h2><p>管理code参考:https://xv6.dgs.zone/labs/use_git/git1.html</p><p>主要说一说怎么提交。</p><p>首先在这个网站注册账号并复制<code>api-KEY</code>:https://6828.scripts.mit.edu/2021/handin.py/</p><p>将所有改动提交到<code>github</code>后,在根目录输入:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">make handin<br></code></pre></td></tr></table></figure><p>之后会提示让输入<code>api-key</code>,复制进去回车就行。如果<code>api-key</code>修改了,可以在文件<code>myapi.key</code>同步修改即可。</p>]]></content>
<categories>
<category>6.S081</category>
</categories>
<tags>
<tag>6.S081</tag>
</tags>
</entry>
<entry>
<title>std::chrono时间库总结</title>
<link href="/2024/01/20/std-chrono%E6%97%B6%E9%97%B4%E5%BA%93%E6%80%BB%E7%BB%93/"/>
<url>/2024/01/20/std-chrono%E6%97%B6%E9%97%B4%E5%BA%93%E6%80%BB%E7%BB%93/</url>
<content type="html"><![CDATA[<h2 id="基本概念">基本概念</h2><p>C++11中引入了一个新的关于时间的库,所有内容都包括在<code>std::chrono</code>中,使用时直接引用头文件<code>#include<chrono></code>和命名空间<code>std::chrono</code>即可。</p><p><code>std::chrono</code>库中主要有3个概念,时钟(Clocks)、时间段(Duration)、时间点(timePoint)。</p><h3 id="时钟clocks">时钟(Clocks)</h3><p>顾名思义,就是和计时的相关类。主要就有3个,分别是:</p><ol type="1"><li><p>系统时钟(system_clock),和操作系统的系统时间同步的时钟,一般是unix时间,即从1970年1月1日到当前系统时间的时间间隔。例如系统时间是1970年1月2日23:59:59,那么返回的值即为<spanclass="math inline">\(24*60*60=86400\)</span>秒。如果系统时间发生改变,相应的值也会发生改变。一般就是用来读取当前的系统时间。</p></li><li><p>单调时钟(steady_clock),就类似秒表,每一次调用返回的值都会大于上一次调用的值,和系统的时间无关。用于程序计时尽量用steady_clock,可以防止在不同PC上运行导致的返回时间不准确。</p></li><li><p>高精度时钟(high_resolution_clock),提供拥有最短计数周期的时钟,在某些编译器中可能是std::steady_clock或std::system_clock的别名,并且在不同编译器中的实现可能有巨大差异,应该尽量避免使用。</p></li></ol><p>一般来说,我们使用的都是system_clock,steady_clock相比system_clock就少了两个静态成员函数:<code>to_time_t</code>和<code>from_time_t</code>。</p><p>下面是两个使用示例:</p><p>使用system_clock读取系统时间。</p><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></pre></td><td class="code"><pre><code class="hljs C++"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><chrono></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><ctime></span></span><br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span><br><span class="hljs-function"></span>{<br> <span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std::chrono;<br><br> duration<<span class="hljs-type">int</span>, std::ratio<<span class="hljs-number">60</span>*<span class="hljs-number">60</span>*<span class="hljs-number">24</span>>> oneDay{<span class="hljs-number">1</span>}; <span class="hljs-comment">// 长度为1天的时间段,duration类在下面会说。</span><br> system_clock::time_point today = system_clock::<span class="hljs-built_in">now</span>();<br> system_clock::time_point tomorrow = today + oneDay; <span class="hljs-comment">// 都重载了加减运算符</span><br><br> <span class="hljs-comment">// 需要转换为time_t类型</span><br> std::<span class="hljs-type">time_t</span> curTime = system_clock::<span class="hljs-built_in">to_time_t</span>(today);<br> std::cout << <span class="hljs-string">"Today is: "</span> << <span class="hljs-built_in">ctime</span>(&curTime);<br><br> std::<span class="hljs-type">time_t</span> nextTime = system_clock::<span class="hljs-built_in">to_time_t</span>(tomorrow);<br> std::cout << <span class="hljs-string">"tomorrow is: "</span> << <span class="hljs-built_in">ctime</span>(&nextTime);<br>}<br></code></pre></td></tr></table></figure><p>输出: <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">Today is: Sat Jan 20 01:46:56 2024<br>tomorrow is: Sun Jan 21 01:46:56 2024<br></code></pre></td></tr></table></figure></p><p>记录函数的运行时间一般使用steady_clock: <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></pre></td><td class="code"><pre><code class="hljs C++"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><chrono></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><ctime></span></span><br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span><br><span class="hljs-function"></span>{<br> <span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std::chrono;<br><br> steady_clock::time_point start = steady_clock::<span class="hljs-built_in">now</span>();<br> <br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>;i < <span class="hljs-number">1000</span>;i ++) std::cout << <span class="hljs-string">'*'</span>;<br> std::cout << std::endl;<br><br> steady_clock::time_point end = steady_clock::<span class="hljs-built_in">now</span>();<br><br> duration<<span class="hljs-type">double</span>, std::ratio<<span class="hljs-number">1</span>>> period = duration_cast<duration<<span class="hljs-type">double</span>>>(end - start);<br> <span class="hljs-comment">// 直接调用构造函数初始化也行</span><br> <span class="hljs-comment">// duration<double, std::ratio<1>> period(end-start);</span><br> std::cout << <span class="hljs-string">"Took "</span> << period.<span class="hljs-built_in">count</span>() << <span class="hljs-string">" seconds"</span> << std::endl;<br>}<br></code></pre></td></tr></table></figure> 输出:<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">Took 2.7105e-05 seconds<br></code></pre></td></tr></table></figure>注意,steady_clock没有<code>to_time_t</code>和<code>from_time_t</code>,只能提供基本计时操作。</p><h3 id="时间段durations">时间段(Durations)</h3><p>顾名思义就是表示一段持续的时间,比如1s、1min、1day等,需要指定单位,所以Duration需要提供一个模板来指定。</p><p>他的定义如下: <figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs C++"><span class="hljs-keyword">template</span><<span class="hljs-keyword">class</span> <span class="hljs-title class_">Rep</span>, <span class="hljs-keyword">class</span> <span class="hljs-title class_">Period</span> = std::ratio<<span class="hljs-number">1</span>>> <span class="hljs-keyword">class</span> duration;<br></code></pre></td></tr></table></figure>其中<code>Period</code>就是用来表示时间段的单位,比如天、分钟、秒等。<code>Rep</code>就是实际用来存储数据类型。比如10秒,则<code>Period</code>就为<code>std::ratio<1></code>,<code>Rep</code>就为<code>int</code>,数值为10。如果要表示10.0秒,则<code>Rep</code>就为<code>double</code>。一般</p><p><code>Period</code>类是<code>std::ratio</code>的别名,也就是一个分数(比率),定义如下:<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs C++"><span class="hljs-keyword">template</span> <<span class="hljs-type">intmax_t</span> N, <span class="hljs-type">intmax_t</span> D = <span class="hljs-number">1</span>> <span class="hljs-keyword">class</span> ratio;<br></code></pre></td></tr></table></figure>N表示分子,D表示分母,用秒作为基本单位。例如当N=1,D=1时候,就是1/1 =1s,当N=1,D=1000时,就是1/1000 = 1ms,当N=60,D=1时,就是60/1 =1min。</p><p>标准库定义了一些常见的时间段: <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></pre></td><td class="code"><pre><code class="hljs C++"><span class="hljs-keyword">using</span> nanoseconds = duration<_GLIBCXX_CHRONO_INT64_T, ratio<<span class="hljs-number">1</span>,<span class="hljs-number">1000000000</span>>>; <span class="hljs-comment">// 纳秒,使用至少64位的有符号整数存储</span><br><span class="hljs-keyword">using</span> microseconds = duration<_GLIBCXX_CHRONO_INT64_T, ratio<<span class="hljs-number">1</span>, <span class="hljs-number">1000000</span>>>; <span class="hljs-comment">// 微秒,使用至少55位的有符号整数存储</span><br><span class="hljs-keyword">using</span> milliseconds = duration<_GLIBCXX_CHRONO_INT64_T, ratio<<span class="hljs-number">1</span>, <span class="hljs-number">1000</span>>>; <span class="hljs-comment">// 毫秒,使用至少45位的有符号整数存储</span><br><span class="hljs-keyword">using</span> seconds = duration<_GLIBCXX_CHRONO_INT64_T>;<br><span class="hljs-comment">// 秒,使用长度不小于35位的有符号整数存储</span><br><span class="hljs-keyword">using</span> minutes = duration<_GLIBCXX_CHRONO_INT64_T, ratio<<span class="hljs-number">60</span>, <span class="hljs-number">1</span>>>; <span class="hljs-comment">// 分钟, 使用长度不小于29为的有符号整数存储。</span><br></code></pre></td></tr></table></figure>其他的参考:https://cplusplus.com/reference/chrono/duration/,懒得写了。</p><p>对于duration来说,一般有两种操作:</p><ol type="1"><li>时间的增减。</li></ol><p>duration重载了大多数运算符,可以直接进行运算: <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></pre></td><td class="code"><pre><code class="hljs C++"><span class="hljs-comment">// duration operators</span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><ratio></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><chrono></span></span><br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span> <span class="hljs-params">()</span></span><br><span class="hljs-function"></span>{<br><br> std::chrono::duration<<span class="hljs-type">int</span>> foo{};<br> std::<span class="hljs-function">chrono::duration<<span class="hljs-type">int</span>> <span class="hljs-title">bar</span> <span class="hljs-params">(<span class="hljs-number">10</span>)</span></span>;<br><br> <span class="hljs-comment">// counts: foo bar</span><br> <span class="hljs-comment">// --- ---</span><br> foo = bar; <span class="hljs-comment">// 10 10</span><br> foo = foo + bar; <span class="hljs-comment">// 20 10</span><br> ++foo; <span class="hljs-comment">// 21 10</span><br> --bar; <span class="hljs-comment">// 21 9</span><br> foo *= <span class="hljs-number">2</span>; <span class="hljs-comment">// 42 9</span><br> foo /= <span class="hljs-number">3</span>; <span class="hljs-comment">// 14 9</span><br> bar += ( foo % bar ); <span class="hljs-comment">// 14 14</span><br><br> std::cout << std::boolalpha;<br> std::cout << <span class="hljs-string">"foo==bar: "</span> << (foo==bar) << std::endl; <span class="hljs-comment">// == 运算符比较两个时间段的值</span><br> std::cout << <span class="hljs-string">"foo: "</span> << foo.<span class="hljs-built_in">count</span>() << std::endl;<br> std::cout << <span class="hljs-string">"bar: "</span> << bar.<span class="hljs-built_in">count</span>() << std::endl;<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure> 输出:<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">foo==bar: <span class="hljs-literal">true</span><br>foo: 14<br>bar: 14<br></code></pre></td></tr></table></figure> 2. 不同单位时间段的转换可以使用<code>duration_cast</code>函数来转换: <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></pre></td><td class="code"><pre><code class="hljs C++"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><chrono></span></span><br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span> <span class="hljs-params">()</span></span><br><span class="hljs-function"></span>{<br> std::<span class="hljs-function">chrono::seconds <span class="hljs-title">s</span> <span class="hljs-params">(<span class="hljs-number">1</span>)</span></span>; <span class="hljs-comment">// 1 second</span><br> std::chrono::milliseconds ms = std::chrono::<span class="hljs-built_in">duration_cast</span><std::chrono::milliseconds> (s);<br> std::cout << <span class="hljs-string">"ms: "</span> << ms.<span class="hljs-built_in">count</span>() << std::endl;<br> std::cout << <span class="hljs-string">"s: "</span> << s.<span class="hljs-built_in">count</span>() << std::endl;<br><br> ms += std::chrono::<span class="hljs-built_in">milliseconds</span>(<span class="hljs-number">2500</span>); <span class="hljs-comment">// 3500 millisecond</span><br><br> s = std::chrono::<span class="hljs-built_in">duration_cast</span><std::chrono::seconds> (ms);<br><br> std::cout << <span class="hljs-string">"ms: "</span> << ms.<span class="hljs-built_in">count</span>() << std::endl;<br> std::cout << <span class="hljs-string">"s: "</span> << s.<span class="hljs-built_in">count</span>() << std::endl;<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure> 输出:<figure class="highlight plaintext"><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></pre></td><td class="code"><pre><code class="hljs bash:">ms: 1000<br>s: 1<br>ms: 3500<br>s: 3<br></code></pre></td></tr></table></figure></p><h3 id="时间点time_point">时间点(time_point)</h3><p>时间点表示一个确切的时间,例如:2024年1月20日1:49:53。时钟(clocks)返回的值就是一个时间点,两个时间点之间的差值就是一段时间(duration)。</p><p>时间点的定义如下: <figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs C++"><span class="hljs-keyword">template</span><<span class="hljs-keyword">class</span> <span class="hljs-title class_">Clock</span>, <span class="hljs-keyword">class</span> <span class="hljs-title class_">Duration</span> = <span class="hljs-keyword">typename</span> Clock::duration> <span class="hljs-keyword">class</span> time_point;<br></code></pre></td></tr></table></figure>可以看出,标准库将时间点定义为:在基准时钟的起始时间加上一个时间段,一次来表示一个时间点。所以时间点都是基于基准时钟的起始时间来的。</p><p>常用的函数有:</p><ol type="1"><li><code>time_since_epoch()</code>: 返回基于起始时间的时间段。</li><li><code>time_point_cast()</code>:用来将时间点转换为基于同一个时钟,但为不同单位的时间点。</li></ol><p>一般使用时间点的场景就是类似clock的那种,记录两个时间点然后计时,或者转换为时间戳什么的。</p><p>时间点加减时间段返回一个新的时间点,时间点加减一个时间点返回一个时间段,和常识都一样,还是挺好理解。</p>]]></content>
<categories>
<category>modern-cpp</category>
</categories>
<tags>
<tag>modern-cpp</tag>
</tags>
</entry>
<entry>
<title>The 2022 ICPC Asia Xian Regional Contest</title>
<link href="/2023/09/06/The%202022%20ICPC%20Asia%20Xian%20Regional%20Contest/"/>
<url>/2023/09/06/The%202022%20ICPC%20Asia%20Xian%20Regional%20Contest/</url>
<content type="html"><![CDATA[<p>B题网络流看了好久都没看明白,这场撑死也只能6题T_T,但是6题还可能铁。🐔了。</p><hr /><h2 id="c---clone-ranran-cf104077-c"><ahref="https://codeforces.com/gym/104077/problem/C">C - Clone Ranran(CF104077 C)</a></h2><h3 id="题目大意">题目大意</h3><p><++></p><h3 id="解题思路">解题思路</h3><p>肯定是先复制然后一遍出题最优,枚举复制就行。</p><details><summary>神奇的代码</summary><figure class="highlight cpp"><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><bits/stdc++.h></span></span><br><br><span class="hljs-keyword">using</span> i64 = <span class="hljs-type">long</span> <span class="hljs-type">long</span>;<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">solve</span><span class="hljs-params">()</span> </span>{<br> i64 a, b, c;<br> std::cin >> a >> b >> c;<br><br> i64 ans = (<span class="hljs-number">1LL</span> << <span class="hljs-number">60</span>);<br> i64 add = <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">for</span> (i64 i = <span class="hljs-number">1</span>;i <= c + c;i <<= <span class="hljs-number">1</span>) {<br> ans = std::<span class="hljs-built_in">min</span>(ans, <span class="hljs-number">1LL</span> * ((c + i - <span class="hljs-number">1</span>) / i) * b + add);<br> add += a;<br> }<br> <br> std::cout << ans << <span class="hljs-string">'\n'</span>;<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{<br> std::cin.<span class="hljs-built_in">tie</span>(<span class="hljs-literal">nullptr</span>)-><span class="hljs-built_in">sync_with_stdio</span>(<span class="hljs-literal">false</span>);<br><br> <span class="hljs-type">int</span> tt = <span class="hljs-number">1</span>;<br> std::cin >> tt;<br> <span class="hljs-keyword">while</span> (tt --) <span class="hljs-built_in">solve</span>();<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></details><p><br></p><hr /><h2 id="f---hotel-cf104077-f"><ahref="https://codeforces.com/gym/104077/problem/F">F - Hotel (CF104077F)</a></h2><h3 id="题目大意-1">题目大意</h3><p><++></p><h3 id="解题思路-1">解题思路</h3><p>贪心</p><details><summary>神奇的代码</summary><figure class="highlight cpp"><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><bits/stdc++.h></span></span><br><br><span class="hljs-keyword">using</span> i64 = <span class="hljs-type">long</span> <span class="hljs-type">long</span>;<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">solve</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-type">int</span> n, c1, c2;<br> std::cin >> n >> c1 >> c2;<br><br> i64 ans = <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>;i < n;i ++) {<br> std::string s;<br> std::cin >> s;<br> <span class="hljs-keyword">if</span> (c1 * <span class="hljs-number">2</span> <= c2) {<br> ans += <span class="hljs-number">3</span> * c1;<br> } <span class="hljs-keyword">else</span> {<br> <span class="hljs-keyword">if</span> (s[<span class="hljs-number">0</span>] == s[<span class="hljs-number">1</span>] || s[<span class="hljs-number">0</span>] == s[<span class="hljs-number">2</span>] || s[<span class="hljs-number">1</span>] == s[<span class="hljs-number">2</span>]) ans += c2 + std::<span class="hljs-built_in">min</span>(c1, c2);<br> <span class="hljs-keyword">else</span> ans += std::<span class="hljs-built_in">min</span>(<span class="hljs-number">3</span> * c1, <span class="hljs-number">3</span> * c2);<br> }<br> }<br><br> std::cout << ans << std::endl;<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{<br> std::cin.<span class="hljs-built_in">tie</span>(<span class="hljs-literal">nullptr</span>)-><span class="hljs-built_in">sync_with_stdio</span>(<span class="hljs-literal">false</span>);<br><br> <span class="hljs-type">int</span> tt = <span class="hljs-number">1</span>;<br> <span class="hljs-comment">// std::cin >> tt;</span><br> <span class="hljs-keyword">while</span> (tt --) <span class="hljs-built_in">solve</span>();<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></details><p><br></p><hr /><h2 id="g---perfect-word-cf104077-g"><ahref="https://codeforces.com/gym/104077/problem/G">G - Perfect Word(CF104077 G)</a></h2><h3 id="题目大意-2">题目大意</h3><p><++></p><h3 id="解题思路-2">解题思路</h3><p>可以发现,当一个字符串是完美字符串时,他的任意一个子串也必须是完美字符串。判断掐头去尾的两个字符串是否是完美的就可以。</p><p>将所有字符串按长度从小到大排序,依次判断哪些是完美字符串维护最大值即可。</p><details><summary>神奇的代码</summary><figure class="highlight cpp"><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><bits/stdc++.h></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><string></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><unordered_map></span></span><br><br><span class="hljs-keyword">using</span> i64 = <span class="hljs-type">long</span> <span class="hljs-type">long</span>;<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">solve</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-type">int</span> n;<br> std::cin >> n;<br><br> <span class="hljs-function">std::vector<std::string> <span class="hljs-title">s</span><span class="hljs-params">(n)</span></span>;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>;i < n;i ++) {<br> std::cin >> s[i];<br> }<br><br> std::<span class="hljs-built_in">sort</span>(s.<span class="hljs-built_in">begin</span>(), s.<span class="hljs-built_in">end</span>(), [&](std::string &a, std::string &b) {<br> <span class="hljs-keyword">return</span> a.<span class="hljs-built_in">size</span>() < b.<span class="hljs-built_in">size</span>();<br> });<br><br> <span class="hljs-type">int</span> ans = <span class="hljs-number">0</span>;<br> std::map<std::string, <span class="hljs-type">bool</span>> mp;<br><br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>;i < n;i ++) {<br> <span class="hljs-keyword">if</span> (s[i].<span class="hljs-built_in">size</span>() == <span class="hljs-number">1</span>) {<br> mp[s[i]] = <span class="hljs-literal">true</span>;<br> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (mp.<span class="hljs-built_in">count</span>(s[i].<span class="hljs-built_in">substr</span>(<span class="hljs-number">0</span>, s[i].<span class="hljs-built_in">size</span>() - <span class="hljs-number">1</span>)) && mp.<span class="hljs-built_in">count</span>(s[i].<span class="hljs-built_in">substr</span>(<span class="hljs-number">1</span>))) {<br> mp[s[i]] = <span class="hljs-literal">true</span>;<br> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">continue</span>;<br> ans = std::<span class="hljs-built_in">max</span>(ans, <span class="hljs-built_in">int</span>(s[i].<span class="hljs-built_in">size</span>()));<br> }<br><br> std::cout << ans << std::endl;<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{<br> std::cin.<span class="hljs-built_in">tie</span>(<span class="hljs-literal">nullptr</span>)-><span class="hljs-built_in">sync_with_stdio</span>(<span class="hljs-literal">false</span>);<br><br> <span class="hljs-type">int</span> tt = <span class="hljs-number">1</span>;<br> <span class="hljs-comment">// std::cin >> tt;</span><br> <span class="hljs-keyword">while</span> (tt --) <span class="hljs-built_in">solve</span>();<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></details><p><br></p><hr /><h2 id="j---strange-sum-cf104077-j"><ahref="https://codeforces.com/gym/104077/problem/J">J - Strange Sum(CF104077 J)</a></h2><h3 id="题目大意-3">题目大意</h3><p><++></p><h3 id="解题思路-3">解题思路</h3><p>去<span class="math inline">\(i\)</span>为<spanclass="math inline">\(n\)</span>显然可以取到整个区间。设<spanclass="math inline">\(a\)</span>为最大值,<spanclass="math inline">\(b\)</span>为次大值,答案一定为:</p><p><span class="math inline">\(ans = max(0, a, a + b)\)</span></p><details><summary>神奇的代码</summary><figure class="highlight cpp"><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><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><bits/stdc++.h></span></span><br><br><span class="hljs-keyword">using</span> i64 = <span class="hljs-type">long</span> <span class="hljs-type">long</span>;<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">solve</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-type">int</span> n;<br> std::cin >> n;<br><br> <span class="hljs-function">std::vector<i64> <span class="hljs-title">a</span><span class="hljs-params">(n)</span></span>;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>;i < n;i ++) {<br> std::cin >> a[i];<br> }<br><br> std::<span class="hljs-built_in">sort</span>(a.<span class="hljs-built_in">begin</span>(), a.<span class="hljs-built_in">end</span>(), std::<span class="hljs-built_in">greater</span><<span class="hljs-type">int</span>>());<br> i64 ans = <span class="hljs-number">0</span>;<br> ans = std::<span class="hljs-built_in">max</span>(ans, a[<span class="hljs-number">0</span>] + a[<span class="hljs-number">1</span>]);<br> ans = std::<span class="hljs-built_in">max</span>(ans, a[<span class="hljs-number">0</span>]);<br><br> std::cout << ans << std::endl;<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{<br> std::cin.<span class="hljs-built_in">tie</span>(<span class="hljs-literal">nullptr</span>)-><span class="hljs-built_in">sync_with_stdio</span>(<span class="hljs-literal">false</span>);<br><br> <span class="hljs-type">int</span> tt = <span class="hljs-number">1</span>;<br> <span class="hljs-comment">// std::cin >> tt;</span><br> <span class="hljs-keyword">while</span> (tt --) <span class="hljs-built_in">solve</span>();<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></details><p><br></p><hr /><h2 id="l---tree-cf104077-l"><ahref="https://codeforces.com/gym/104077/problem/L">L - Tree (CF104077L)</a></h2><h3 id="题目大意-4">题目大意</h3><p><++></p><h3 id="解题思路-4">解题思路</h3><p>每个好集合要么是一条链,要么是所有叶子节点。</p><p>可以一层一层的叶子节点删上去,不断维护当前有多少条链+删的层数最小即可。</p><p>求链用长链剖分也行,直接dfs判断当前层有多少叶子节点也行。</p><p>时间复杂度<span class="math inline">\(o(n)\)</span>.</p><details><summary>神奇的代码</summary><figure class="highlight cpp"><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><bits/stdc++.h></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><functional></span></span><br><br><span class="hljs-keyword">using</span> i64 = <span class="hljs-type">long</span> <span class="hljs-type">long</span>;<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">solve</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-type">int</span> n;<br> std::cin >> n;<br><br> std::vector<std::vector<<span class="hljs-type">int</span>>> <span class="hljs-built_in">g</span>(n + <span class="hljs-number">1</span>, std::<span class="hljs-built_in">vector</span><<span class="hljs-type">int</span>>());<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">2</span>;i <= n;i ++) {<br> <span class="hljs-type">int</span> x;<br> std::cin >> x;<br> g[i].<span class="hljs-built_in">emplace_back</span>(x);<br> g[x].<span class="hljs-built_in">emplace_back</span>(i);<br> }<br><br> <span class="hljs-function">std::vector<<span class="hljs-type">int</span>> <span class="hljs-title">cnt</span><span class="hljs-params">(n + <span class="hljs-number">1</span>)</span>, <span class="hljs-title">dep</span><span class="hljs-params">(n + <span class="hljs-number">1</span>)</span></span>;<br> std::function<<span class="hljs-type">void</span>(<span class="hljs-type">int</span>, <span class="hljs-type">int</span>)> dfs = [&](<span class="hljs-type">int</span> u, <span class="hljs-type">int</span> fa) {<br> dep[u] = <span class="hljs-number">1</span>;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> v : g[u]) {<br> <span class="hljs-keyword">if</span> (v == fa) <span class="hljs-keyword">continue</span>;<br> <span class="hljs-built_in">dfs</span>(v, u);<br> dep[u] = std::<span class="hljs-built_in">max</span>(dep[u], dep[v] + <span class="hljs-number">1</span>);<br> }<br> cnt[dep[u]] ++;<br> };<br><br> <span class="hljs-built_in">dfs</span>(<span class="hljs-number">1</span>, <span class="hljs-number">-1</span>);<br><br> <span class="hljs-type">int</span> ans = (<span class="hljs-number">1</span> << <span class="hljs-number">30</span>);<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">1</span>;i <= n;i ++) {<br> ans = std::<span class="hljs-built_in">min</span>(ans, i + cnt[i] - <span class="hljs-number">1</span>);<br> }<br><br> std::cout << ans << <span class="hljs-string">'\n'</span>;<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{<br> std::cin.<span class="hljs-built_in">tie</span>(<span class="hljs-literal">nullptr</span>)-><span class="hljs-built_in">sync_with_stdio</span>(<span class="hljs-literal">false</span>);<br><br> <span class="hljs-type">int</span> tt = <span class="hljs-number">1</span>;<br> std::cin >> tt;<br> <span class="hljs-keyword">while</span> (tt --) <span class="hljs-built_in">solve</span>();<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></details><p><br></p><hr />]]></content>
<categories>
<category>算法题解</category>
</categories>
<tags>
<tag>codeforces</tag>
</tags>
</entry>
<entry>
<title>AtCoder Beginner Contest 317</title>
<link href="/2023/09/01/AtCoder%20Beginner%20Contest%20317/"/>
<url>/2023/09/01/AtCoder%20Beginner%20Contest%20317/</url>
<content type="html"><![CDATA[<h2 id="a---potions-abc317-a"><ahref="https://atcoder.jp/contests/abc317/tasks/abc317_a">A - Potions(abc317 A)</a></h2><h3 id="题目大意">题目大意</h3><h3 id="解题思路">解题思路</h3><details><summary>神奇的代码</summary><figure class="highlight cpp"><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><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><bits/stdc++.h></span></span><br><br><span class="hljs-keyword">using</span> i64 = <span class="hljs-type">long</span> <span class="hljs-type">long</span>;<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">solve</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-type">int</span> n, h, x;<br> std::cin >> n >> h >> x;<br><br> <span class="hljs-function">std::vector<<span class="hljs-type">int</span>> <span class="hljs-title">a</span><span class="hljs-params">(n)</span></span>;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>;i < n;i ++) {<br> std::cin >> a[i];<br> }<br><br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>;i < n;i ++) {<br> <span class="hljs-keyword">if</span> (h + a[i] >= x) {<br> std::cout << i + <span class="hljs-number">1</span> << <span class="hljs-string">'\n'</span>;<br> <span class="hljs-keyword">return</span> ;<br> }<br> }<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{<br> std::cin.<span class="hljs-built_in">tie</span>(<span class="hljs-literal">nullptr</span>)-><span class="hljs-built_in">sync_with_stdio</span>(<span class="hljs-literal">false</span>);<br><br> <span class="hljs-type">int</span> tt = <span class="hljs-number">1</span>;<br> std::cin >> tt;<br> <span class="hljs-keyword">while</span> (tt --) <span class="hljs-built_in">solve</span>();<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></details><p><br></p><hr /><h2 id="b---missingno.-abc317-b"><ahref="https://atcoder.jp/contests/abc317/tasks/abc317_b">B - MissingNo.(abc317 B)</a></h2><h3 id="题目大意-1">题目大意</h3><p><++></p><h3 id="解题思路-1">解题思路</h3><p><++></p><details><summary>神奇的代码</summary><figure class="highlight cpp"><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><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><bits/stdc++.h></span></span><br><br><span class="hljs-keyword">using</span> i64 = <span class="hljs-type">long</span> <span class="hljs-type">long</span>;<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">solve</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-type">int</span> n;<br> std::cin >> n;<br><br> <span class="hljs-function">std::vector<<span class="hljs-type">int</span>> <span class="hljs-title">a</span><span class="hljs-params">(n)</span></span>;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>;i < n;i ++) {<br> std::cin >> a[i];<br> }<br><br> std::<span class="hljs-built_in">sort</span>(a.<span class="hljs-built_in">begin</span>(), a.<span class="hljs-built_in">end</span>());<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">1</span>;i < n;i ++) {<br> <span class="hljs-keyword">if</span> (a[i] != a[i - <span class="hljs-number">1</span>] + <span class="hljs-number">1</span>) {<br> std::cout << a[i - <span class="hljs-number">1</span>] + <span class="hljs-number">1</span> << <span class="hljs-string">'\n'</span>;<br> <span class="hljs-keyword">return</span> ;<br> }<br> }<br><br> std::cout << a[<span class="hljs-number">0</span>] - <span class="hljs-number">1</span> << <span class="hljs-string">'\n'</span>;<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{<br> std::cin.<span class="hljs-built_in">tie</span>(<span class="hljs-literal">nullptr</span>)-><span class="hljs-built_in">sync_with_stdio</span>(<span class="hljs-literal">false</span>);<br><br> <span class="hljs-type">int</span> tt = <span class="hljs-number">1</span>;<br> <span class="hljs-comment">// std::cin >> tt;</span><br> <span class="hljs-keyword">while</span> (tt --) <span class="hljs-built_in">solve</span>();<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></details><p><br></p><hr /><h2 id="c---remembering-the-days-abc317-c"><ahref="https://atcoder.jp/contests/abc317/tasks/abc317_c">C - Rememberingthe Days (abc317 C)</a></h2><h3 id="题目大意-2">题目大意</h3><p><++></p><h3 id="解题思路-2">解题思路</h3><p>很裸的一个图上dfs,一直维护最大值就行。</p><details><summary>神奇的代码</summary><figure class="highlight cpp"><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><bits/stdc++.h></span></span><br><br><span class="hljs-keyword">using</span> i64 = <span class="hljs-type">long</span> <span class="hljs-type">long</span>;<br><span class="hljs-keyword">using</span> PII = std::pair<<span class="hljs-type">int</span>, <span class="hljs-type">int</span>>;<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">solve</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-type">int</span> n, m;<br> std::cin >> n >> m;<br><br> std::vector<std::vector<PII>> <span class="hljs-built_in">g</span>(n + <span class="hljs-number">1</span>, std::<span class="hljs-built_in">vector</span><PII>());<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>;i < m;i ++) {<br> <span class="hljs-type">int</span> u, v, w;<br> std::cin >> u >> v >> w;<br> g[u].<span class="hljs-built_in">emplace_back</span>(v, w);<br> g[v].<span class="hljs-built_in">emplace_back</span>(u, w);<br> }<br><br> i64 ans = <span class="hljs-number">0</span>;<br> std::bitset<20> vis;<br> std::function<<span class="hljs-type">void</span>(<span class="hljs-type">int</span>, <span class="hljs-type">int</span>)> dfs = [&](<span class="hljs-type">int</span> u, i64 res) {<br> ans = std::<span class="hljs-built_in">max</span>(ans, res);<br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span> &[v, w] : g[u]) {<br> <span class="hljs-keyword">if</span> (vis[v]) <span class="hljs-keyword">continue</span>;<br> vis[v] = <span class="hljs-literal">true</span>;<br> <span class="hljs-built_in">dfs</span>(v, res + w);<br> vis[v] = <span class="hljs-literal">false</span>;<br> }<br> };<br><br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">1</span>;i <= n;i ++) {<br> vis.<span class="hljs-built_in">reset</span>();<br> vis[i] = <span class="hljs-literal">true</span>;<br> <span class="hljs-built_in">dfs</span>(i, <span class="hljs-number">0</span>);<br> }<br><br> std::cout << ans << std::endl;<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{<br> std::cin.<span class="hljs-built_in">tie</span>(<span class="hljs-literal">nullptr</span>)-><span class="hljs-built_in">sync_with_stdio</span>(<span class="hljs-literal">false</span>);<br> <br> <span class="hljs-type">int</span> tt = <span class="hljs-number">1</span>;<br> <span class="hljs-comment">// std::cin >> tt;</span><br> <span class="hljs-keyword">while</span> (tt --) <span class="hljs-built_in">solve</span>();<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></details><p><br></p><hr /><h2 id="d---president-abc317-d"><ahref="https://atcoder.jp/contests/abc317/tasks/abc317_d">D - President(abc317 D)</a></h2><h3 id="题目大意-3">题目大意</h3><p><++></p><h3 id="解题思路-3">解题思路</h3><p>每个地区都有叛变或不叛变两种情况,所以考虑用背包。</p><p>由于Z的和很小,所以设<spanclass="math inline">\(dp[i][j]\)</span>为前<spanclass="math inline">\(i\)</span>个地区收获了<spanclass="math inline">\(j\)</span>张选票,需要叛变的人数。</p><p>然后正常<span class="math inline">\(0/1\)</span>就行。</p><details><summary>神奇的代码</summary><figure class="highlight cpp"><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><algorithm></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><bits/stdc++.h></span></span><br><br><span class="hljs-keyword">using</span> i64 = <span class="hljs-type">long</span> <span class="hljs-type">long</span>;<br><span class="hljs-keyword">using</span> PII = std::pair<<span class="hljs-type">int</span>, <span class="hljs-type">int</span>>;<br><br><span class="hljs-keyword">constexpr</span> i64 INF = (<span class="hljs-number">1LL</span> << <span class="hljs-number">60</span>);<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">solve</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-type">int</span> n;<br> std::cin >> n;<br><br> i64 sum = <span class="hljs-number">0</span>;<br> <span class="hljs-function">std::vector<i64> <span class="hljs-title">x</span><span class="hljs-params">(n + <span class="hljs-number">1</span>)</span>, <span class="hljs-title">y</span><span class="hljs-params">(n + <span class="hljs-number">1</span>)</span>, <span class="hljs-title">z</span><span class="hljs-params">(n + <span class="hljs-number">1</span>)</span></span>;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">1</span>;i <= n;i ++) {<br> std::cin >> x[i] >> y[i] >> z[i];<br> sum += z[i];<br> }<br><br> <span class="hljs-function">std::vector<i64> <span class="hljs-title">dp</span><span class="hljs-params">(sum + <span class="hljs-number">1</span>, INF)</span></span>;<br> dp[<span class="hljs-number">0</span>] = <span class="hljs-number">0</span>;<br><br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">1</span>;i <= n;i ++) {<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> j = sum;j >= z[i];j --) {<br> i64 cost = std::<span class="hljs-built_in">max</span>(<span class="hljs-number">0LL</span>, (x[i] + y[i] + <span class="hljs-number">1</span>) / <span class="hljs-number">2</span> - x[i]);<br> dp[j] = std::<span class="hljs-built_in">min</span>(dp[j], dp[j - z[i]] + cost);<br> }<br> }<br><br> i64 ans = *std::<span class="hljs-built_in">min_element</span>(dp.<span class="hljs-built_in">begin</span>() + (sum + <span class="hljs-number">1</span>) / <span class="hljs-number">2</span>, dp.<span class="hljs-built_in">end</span>());<br> std::cout << ans << std::endl;<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{<br> std::cin.<span class="hljs-built_in">tie</span>(<span class="hljs-literal">nullptr</span>)-><span class="hljs-built_in">sync_with_stdio</span>(<span class="hljs-literal">false</span>);<br><br> <span class="hljs-type">int</span> tt = <span class="hljs-number">1</span>;<br> <span class="hljs-comment">// std::cin >> tt;</span><br> <span class="hljs-keyword">while</span> (tt --) <span class="hljs-built_in">solve</span>();<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></details><p><br></p><hr /><h2 id="e---avoid-eye-contact-abc317-e"><ahref="https://atcoder.jp/contests/abc317/tasks/abc317_e">E - Avoid EyeContact (abc317 E)</a></h2><h3 id="题目大意-4">题目大意</h3><p><++></p><h3 id="解题思路-4">解题思路</h3><p>奶奶的还是一题dfs,只不过非常💩。认真判条件就行。</p><details><summary>神奇的代码</summary><figure class="highlight cpp"><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><algorithm></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><bits/stdc++.h></span></span><br><br><span class="hljs-keyword">using</span> i64 = <span class="hljs-type">long</span> <span class="hljs-type">long</span>;<br><span class="hljs-keyword">using</span> PII = std::pair<<span class="hljs-type">int</span>, <span class="hljs-type">int</span>>;<br><br><span class="hljs-keyword">constexpr</span> i64 INF = (<span class="hljs-number">1LL</span> << <span class="hljs-number">60</span>);<br><span class="hljs-keyword">constexpr</span> std::array<<span class="hljs-type">int</span>, 4> dy = {<span class="hljs-number">1</span>, <span class="hljs-number">-1</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>};<br><span class="hljs-keyword">constexpr</span> std::array<<span class="hljs-type">int</span>, 4> dx = {<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">-1</span>};<br><br><span class="hljs-keyword">struct</span> <span class="hljs-title class_">node</span> {<br> <span class="hljs-type">int</span> x, y, step;<br> <span class="hljs-built_in">node</span>() = <span class="hljs-keyword">default</span>;<br> <span class="hljs-built_in">node</span>(<span class="hljs-type">int</span> x, <span class="hljs-type">int</span> y, <span class="hljs-type">int</span> step) : <span class="hljs-built_in">x</span>(x), <span class="hljs-built_in">y</span>(y), <span class="hljs-built_in">step</span>(step) {}<br>};<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">solve</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-type">int</span> n, m;<br> std::cin >> n >> m;<br><br> <span class="hljs-type">int</span> sx = {}, sy = {}, ex = {}, ey = {};<br> std::vector<PII> view;<br> std::vector<std::vector<<span class="hljs-type">char</span>>> <span class="hljs-built_in">a</span>(n + <span class="hljs-number">1</span>, std::<span class="hljs-built_in">vector</span><<span class="hljs-type">char</span>>(m + <span class="hljs-number">1</span>));<br> std::vector<std::vector<<span class="hljs-type">bool</span>>> <span class="hljs-built_in">vis</span>(n + <span class="hljs-number">1</span>, std::<span class="hljs-built_in">vector</span><<span class="hljs-type">bool</span>>(m + <span class="hljs-number">1</span>));<br><br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">1</span>;i <= n;i ++) {<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> j = <span class="hljs-number">1</span>;j <= m;j ++) {<br> std::cin >> a[i][j];<br> <span class="hljs-keyword">if</span> (a[i][j] == <span class="hljs-string">'S'</span>) {<br> sx = i; sy = j;<br> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (a[i][j] == <span class="hljs-string">'G'</span>) {<br> ex = i; ey = j;<br> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (a[i][j] == <span class="hljs-string">'>'</span> || a[i][j] == <span class="hljs-string">'<'</span> || a[i][j] == <span class="hljs-string">'v'</span> || a[i][j] == <span class="hljs-string">'^'</span>) {<br> view.<span class="hljs-built_in">emplace_back</span>(i, j);<br> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(a[i][j] == <span class="hljs-string">'#'</span>) {<br> vis[i][j] = <span class="hljs-literal">true</span>;<br> }<br> }<br> }<br><br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span> &[x, y] : view) {<br> vis[x][y] = <span class="hljs-literal">true</span>;<br> <span class="hljs-keyword">if</span> (a[x][y] == <span class="hljs-string">'>'</span>) {<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = y + <span class="hljs-number">1</span>;i <= m;i ++) {<br> <span class="hljs-keyword">if</span> (a[x][i] == <span class="hljs-string">'.'</span> || a[x][i] == <span class="hljs-string">'S'</span> || a[x][i] == <span class="hljs-string">'G'</span>) vis[x][i] = <span class="hljs-literal">true</span>;<br> <span class="hljs-keyword">else</span> <span class="hljs-keyword">break</span>;<br> }<br> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (a[x][y] == <span class="hljs-string">'<'</span>) {<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = y - <span class="hljs-number">1</span>;i >= <span class="hljs-number">1</span>;i --) {<br> <span class="hljs-keyword">if</span> (a[x][i] == <span class="hljs-string">'.'</span> || a[x][i] == <span class="hljs-string">'S'</span> || a[x][i] == <span class="hljs-string">'G'</span>) vis[x][i] = <span class="hljs-literal">true</span>;<br> <span class="hljs-keyword">else</span> <span class="hljs-keyword">break</span>;<br> }<br> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (a[x][y] == <span class="hljs-string">'v'</span>) {<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = x + <span class="hljs-number">1</span>;i <= n;i ++) {<br> <span class="hljs-keyword">if</span> (a[i][y] == <span class="hljs-string">'.'</span> || a[i][y] == <span class="hljs-string">'S'</span> || a[x][i] == <span class="hljs-string">'G'</span>) vis[i][y] = <span class="hljs-literal">true</span>;<br> <span class="hljs-keyword">else</span> <span class="hljs-keyword">break</span>;<br> }<br> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (a[x][y] == <span class="hljs-string">'^'</span>) {<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = x - <span class="hljs-number">1</span>;i >= <span class="hljs-number">1</span>;i --) {<br> <span class="hljs-keyword">if</span> (a[i][y] == <span class="hljs-string">'.'</span> || a[i][y] == <span class="hljs-string">'S'</span> || a[x][i] == <span class="hljs-string">'G'</span>) vis[i][y] = <span class="hljs-literal">true</span>;<br> <span class="hljs-keyword">else</span> <span class="hljs-keyword">break</span>;<br> }<br> }<br> }<br><br> <span class="hljs-keyword">auto</span> bfs = [&](<span class="hljs-type">void</span>) {<br> std::queue<node> q;<br> q.<span class="hljs-built_in">emplace</span>(sx, sy, <span class="hljs-number">0</span>);<br> <span class="hljs-keyword">while</span> (!q.<span class="hljs-built_in">empty</span>()) {<br> <span class="hljs-keyword">auto</span> [x, y, step] = q.<span class="hljs-built_in">front</span>(); q.<span class="hljs-built_in">pop</span>();<br> <span class="hljs-keyword">if</span> (x == ex && y == ey) <span class="hljs-keyword">return</span> step;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>;i < <span class="hljs-number">4</span>;i ++) {<br> <span class="hljs-type">int</span> xx = x + dx[i], yy = y + dy[i];<br> <span class="hljs-keyword">if</span> (xx == ex && yy == ey) <span class="hljs-keyword">return</span> step + <span class="hljs-number">1</span>;<br> <span class="hljs-keyword">if</span> (xx < <span class="hljs-number">1</span> || xx > n || yy < <span class="hljs-number">1</span> || yy > m) <span class="hljs-keyword">continue</span>;<br> <span class="hljs-keyword">if</span> (vis[xx][yy]) <span class="hljs-keyword">continue</span>;<br> vis[xx][yy] = <span class="hljs-literal">true</span>;<br> q.<span class="hljs-built_in">emplace</span>(xx, yy, step + <span class="hljs-number">1</span>);<br> }<br> }<br> <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;<br> };<br><br> <span class="hljs-type">int</span> ans = <span class="hljs-built_in">bfs</span>();<br> std::cout << ans << std::endl;<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{<br> std::cin.<span class="hljs-built_in">tie</span>(<span class="hljs-literal">nullptr</span>)-><span class="hljs-built_in">sync_with_stdio</span>(<span class="hljs-literal">false</span>);<br><br> <span class="hljs-type">int</span> tt = <span class="hljs-number">1</span>;<br> <span class="hljs-comment">// std::cin >> tt;</span><br> <span class="hljs-keyword">while</span> (tt --) <span class="hljs-built_in">solve</span>();<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></details><p><br></p><hr /><h2 id="f---nim-abc317-f"><ahref="https://atcoder.jp/contests/abc317/tasks/abc317_f">F - Nim (abc317F)</a></h2><h3 id="题目大意-5">题目大意</h3><p><++></p><h3 id="解题思路-5">解题思路</h3><p>计数DP,不知道怎么描述。</p><details><summary>神奇的代码</summary><figure class="highlight cpp"><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><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><bits/stdc++.h></span></span><br><br><span class="hljs-keyword">using</span> i64 = <span class="hljs-type">long</span> <span class="hljs-type">long</span>;<br><span class="hljs-function"><span class="hljs-keyword">template</span><<span class="hljs-keyword">class</span> T></span><br><span class="hljs-function"><span class="hljs-keyword">constexpr</span> T <span class="hljs-title">power</span><span class="hljs-params">(T a, i64 b)</span> </span>{<br> T res = <span class="hljs-number">1</span>;<br> <span class="hljs-keyword">for</span> (; b; b /= <span class="hljs-number">2</span>, a *= a) {<br> <span class="hljs-keyword">if</span> (b % <span class="hljs-number">2</span>) {<br> res *= a;<br> }<br> }<br> <span class="hljs-keyword">return</span> res;<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">constexpr</span> i64 <span class="hljs-title">mul</span><span class="hljs-params">(i64 a, i64 b, i64 p)</span> </span>{<br> i64 res = a * b - <span class="hljs-built_in">i64</span>(<span class="hljs-number">1.L</span> * a * b / p) * p;<br> res %= p;<br> <span class="hljs-keyword">if</span> (res < <span class="hljs-number">0</span>) {<br> res += p;<br> }<br> <span class="hljs-keyword">return</span> res;<br>}<br><span class="hljs-keyword">template</span><i64 P><br><span class="hljs-keyword">struct</span> <span class="hljs-title class_">MLong</span> {<br> i64 x;<br> <span class="hljs-function"><span class="hljs-keyword">constexpr</span> <span class="hljs-title">MLong</span><span class="hljs-params">()</span> : x{</span>} {}<br> <span class="hljs-function"><span class="hljs-keyword">constexpr</span> <span class="hljs-title">MLong</span><span class="hljs-params">(i64 x)</span> : x{</span><span class="hljs-built_in">norm</span>(x % <span class="hljs-built_in">getMod</span>())} {}<br> <br> <span class="hljs-type">static</span> i64 Mod;<br> <span class="hljs-function"><span class="hljs-keyword">constexpr</span> <span class="hljs-type">static</span> i64 <span class="hljs-title">getMod</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-keyword">if</span> (P > <span class="hljs-number">0</span>) {<br> <span class="hljs-keyword">return</span> P;<br> } <span class="hljs-keyword">else</span> {<br> <span class="hljs-keyword">return</span> Mod;<br> }<br> }<br> <span class="hljs-function"><span class="hljs-keyword">constexpr</span> <span class="hljs-type">static</span> <span class="hljs-type">void</span> <span class="hljs-title">setMod</span><span class="hljs-params">(i64 Mod_)</span> </span>{<br> Mod = Mod_;<br> }<br> <span class="hljs-function"><span class="hljs-keyword">constexpr</span> i64 <span class="hljs-title">norm</span><span class="hljs-params">(i64 x)</span> <span class="hljs-type">const</span> </span>{<br> <span class="hljs-keyword">if</span> (x < <span class="hljs-number">0</span>) {<br> x += <span class="hljs-built_in">getMod</span>();<br> }<br> <span class="hljs-keyword">if</span> (x >= <span class="hljs-built_in">getMod</span>()) {<br> x -= <span class="hljs-built_in">getMod</span>();<br> }<br> <span class="hljs-keyword">return</span> x;<br> }<br> <span class="hljs-function"><span class="hljs-keyword">constexpr</span> i64 <span class="hljs-title">val</span><span class="hljs-params">()</span> <span class="hljs-type">const</span> </span>{<br> <span class="hljs-keyword">return</span> x;<br> }<br> <span class="hljs-function"><span class="hljs-keyword">explicit</span> <span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">operator</span> <span class="hljs-title">i64</span><span class="hljs-params">()</span> <span class="hljs-type">const</span> </span>{<br> <span class="hljs-keyword">return</span> x;<br> }<br> <span class="hljs-keyword">constexpr</span> MLong <span class="hljs-keyword">operator</span>-() <span class="hljs-type">const</span> {<br> MLong res;<br> res.x = <span class="hljs-built_in">norm</span>(<span class="hljs-built_in">getMod</span>() - x);<br> <span class="hljs-keyword">return</span> res;<br> }<br> <span class="hljs-function"><span class="hljs-keyword">constexpr</span> MLong <span class="hljs-title">inv</span><span class="hljs-params">()</span> <span class="hljs-type">const</span> </span>{<br> <span class="hljs-built_in">assert</span>(x != <span class="hljs-number">0</span>);<br> <span class="hljs-keyword">return</span> <span class="hljs-built_in">power</span>(*<span class="hljs-keyword">this</span>, <span class="hljs-built_in">getMod</span>() - <span class="hljs-number">2</span>);<br> }<br> <span class="hljs-keyword">constexpr</span> MLong &<span class="hljs-keyword">operator</span>*=(MLong rhs) & {<br> x = <span class="hljs-built_in">mul</span>(x, rhs.x, <span class="hljs-built_in">getMod</span>());<br> <span class="hljs-keyword">return</span> *<span class="hljs-keyword">this</span>;<br> }<br> <span class="hljs-keyword">constexpr</span> MLong &<span class="hljs-keyword">operator</span>+=(MLong rhs) & {<br> x = <span class="hljs-built_in">norm</span>(x + rhs.x);<br> <span class="hljs-keyword">return</span> *<span class="hljs-keyword">this</span>;<br> }<br> <span class="hljs-keyword">constexpr</span> MLong &<span class="hljs-keyword">operator</span>-=(MLong rhs) & {<br> x = <span class="hljs-built_in">norm</span>(x - rhs.x);<br> <span class="hljs-keyword">return</span> *<span class="hljs-keyword">this</span>;<br> }<br> <span class="hljs-keyword">constexpr</span> MLong &<span class="hljs-keyword">operator</span>/=(MLong rhs) & {<br> <span class="hljs-keyword">return</span> *<span class="hljs-keyword">this</span> *= rhs.<span class="hljs-built_in">inv</span>();<br> }<br> <span class="hljs-keyword">friend</span> <span class="hljs-keyword">constexpr</span> MLong <span class="hljs-keyword">operator</span>*(MLong lhs, MLong rhs) {<br> MLong res = lhs;<br> res *= rhs;<br> <span class="hljs-keyword">return</span> res;<br> }<br> <span class="hljs-keyword">friend</span> <span class="hljs-keyword">constexpr</span> MLong <span class="hljs-keyword">operator</span>+(MLong lhs, MLong rhs) {<br> MLong res = lhs;<br> res += rhs;<br> <span class="hljs-keyword">return</span> res;<br> }<br> <span class="hljs-keyword">friend</span> <span class="hljs-keyword">constexpr</span> MLong <span class="hljs-keyword">operator</span>-(MLong lhs, MLong rhs) {<br> MLong res = lhs;<br> res -= rhs;<br> <span class="hljs-keyword">return</span> res;<br> }<br> <span class="hljs-keyword">friend</span> <span class="hljs-keyword">constexpr</span> MLong <span class="hljs-keyword">operator</span>/(MLong lhs, MLong rhs) {<br> MLong res = lhs;<br> res /= rhs;<br> <span class="hljs-keyword">return</span> res;<br> }<br> <span class="hljs-keyword">friend</span> <span class="hljs-keyword">constexpr</span> std::istream &<span class="hljs-keyword">operator</span>>>(std::istream &is, MLong &a) {<br> i64 v;<br> is >> v;<br> a = <span class="hljs-built_in">MLong</span>(v);<br> <span class="hljs-keyword">return</span> is;<br> }<br> <span class="hljs-keyword">friend</span> <span class="hljs-keyword">constexpr</span> std::ostream &<span class="hljs-keyword">operator</span><<(std::ostream &os, <span class="hljs-type">const</span> MLong &a) {<br> <span class="hljs-keyword">return</span> os << a.<span class="hljs-built_in">val</span>();<br> }<br> <span class="hljs-keyword">friend</span> <span class="hljs-keyword">constexpr</span> <span class="hljs-type">bool</span> <span class="hljs-keyword">operator</span>==(MLong lhs, MLong rhs) {<br> <span class="hljs-keyword">return</span> lhs.<span class="hljs-built_in">val</span>() == rhs.<span class="hljs-built_in">val</span>();<br> }<br> <span class="hljs-keyword">friend</span> <span class="hljs-keyword">constexpr</span> <span class="hljs-type">bool</span> <span class="hljs-keyword">operator</span>!=(MLong lhs, MLong rhs) {<br> <span class="hljs-keyword">return</span> lhs.<span class="hljs-built_in">val</span>() != rhs.<span class="hljs-built_in">val</span>();<br> }<br>};<br><br><span class="hljs-keyword">template</span><><br>i64 MLong<<span class="hljs-number">0LL</span>>::Mod = <span class="hljs-built_in">i64</span>(<span class="hljs-number">1E18</span>) + <span class="hljs-number">9</span>;<br><br><span class="hljs-keyword">template</span><<span class="hljs-type">int</span> P><br><span class="hljs-keyword">struct</span> <span class="hljs-title class_">MInt</span> {<br> <span class="hljs-type">int</span> x;<br> <span class="hljs-function"><span class="hljs-keyword">constexpr</span> <span class="hljs-title">MInt</span><span class="hljs-params">()</span> : x{</span>} {}<br> <span class="hljs-function"><span class="hljs-keyword">constexpr</span> <span class="hljs-title">MInt</span><span class="hljs-params">(i64 x)</span> : x{</span><span class="hljs-built_in">norm</span>(x % <span class="hljs-built_in">getMod</span>())} {}<br> <br> <span class="hljs-type">static</span> <span class="hljs-type">int</span> Mod;<br> <span class="hljs-function"><span class="hljs-keyword">constexpr</span> <span class="hljs-type">static</span> <span class="hljs-type">int</span> <span class="hljs-title">getMod</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-keyword">if</span> (P > <span class="hljs-number">0</span>) {<br> <span class="hljs-keyword">return</span> P;<br> } <span class="hljs-keyword">else</span> {<br> <span class="hljs-keyword">return</span> Mod;<br> }<br> }<br> <span class="hljs-function"><span class="hljs-keyword">constexpr</span> <span class="hljs-type">static</span> <span class="hljs-type">void</span> <span class="hljs-title">setMod</span><span class="hljs-params">(<span class="hljs-type">int</span> Mod_)</span> </span>{<br> Mod = Mod_;<br> }<br> <span class="hljs-function"><span class="hljs-keyword">constexpr</span> <span class="hljs-type">int</span> <span class="hljs-title">norm</span><span class="hljs-params">(<span class="hljs-type">int</span> x)</span> <span class="hljs-type">const</span> </span>{<br> <span class="hljs-keyword">if</span> (x < <span class="hljs-number">0</span>) {<br> x += <span class="hljs-built_in">getMod</span>();<br> }<br> <span class="hljs-keyword">if</span> (x >= <span class="hljs-built_in">getMod</span>()) {<br> x -= <span class="hljs-built_in">getMod</span>();<br> }<br> <span class="hljs-keyword">return</span> x;<br> }<br> <span class="hljs-function"><span class="hljs-keyword">constexpr</span> <span class="hljs-type">int</span> <span class="hljs-title">val</span><span class="hljs-params">()</span> <span class="hljs-type">const</span> </span>{<br> <span class="hljs-keyword">return</span> x;<br> }<br> <span class="hljs-function"><span class="hljs-keyword">explicit</span> <span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">operator</span> <span class="hljs-title">int</span><span class="hljs-params">()</span> <span class="hljs-type">const</span> </span>{<br> <span class="hljs-keyword">return</span> x;<br> }<br> <span class="hljs-keyword">constexpr</span> MInt <span class="hljs-keyword">operator</span>-() <span class="hljs-type">const</span> {<br> MInt res;<br> res.x = <span class="hljs-built_in">norm</span>(<span class="hljs-built_in">getMod</span>() - x);<br> <span class="hljs-keyword">return</span> res;<br> }<br> <span class="hljs-function"><span class="hljs-keyword">constexpr</span> MInt <span class="hljs-title">inv</span><span class="hljs-params">()</span> <span class="hljs-type">const</span> </span>{<br> <span class="hljs-built_in">assert</span>(x != <span class="hljs-number">0</span>);<br> <span class="hljs-keyword">return</span> <span class="hljs-built_in">power</span>(*<span class="hljs-keyword">this</span>, <span class="hljs-built_in">getMod</span>() - <span class="hljs-number">2</span>);<br> }<br> <span class="hljs-keyword">constexpr</span> MInt &<span class="hljs-keyword">operator</span>*=(MInt rhs) & {<br> x = <span class="hljs-number">1LL</span> * x * rhs.x % <span class="hljs-built_in">getMod</span>();<br> <span class="hljs-keyword">return</span> *<span class="hljs-keyword">this</span>;<br> }<br> <span class="hljs-keyword">constexpr</span> MInt &<span class="hljs-keyword">operator</span>+=(MInt rhs) & {<br> x = <span class="hljs-built_in">norm</span>(x + rhs.x);<br> <span class="hljs-keyword">return</span> *<span class="hljs-keyword">this</span>;<br> }<br> <span class="hljs-keyword">constexpr</span> MInt &<span class="hljs-keyword">operator</span>-=(MInt rhs) & {<br> x = <span class="hljs-built_in">norm</span>(x - rhs.x);<br> <span class="hljs-keyword">return</span> *<span class="hljs-keyword">this</span>;<br> }<br> <span class="hljs-keyword">constexpr</span> MInt &<span class="hljs-keyword">operator</span>/=(MInt rhs) & {<br> <span class="hljs-keyword">return</span> *<span class="hljs-keyword">this</span> *= rhs.<span class="hljs-built_in">inv</span>();<br> }<br> <span class="hljs-keyword">friend</span> <span class="hljs-keyword">constexpr</span> MInt <span class="hljs-keyword">operator</span>*(MInt lhs, MInt rhs) {<br> MInt res = lhs;<br> res *= rhs;<br> <span class="hljs-keyword">return</span> res;<br> }<br> <span class="hljs-keyword">friend</span> <span class="hljs-keyword">constexpr</span> MInt <span class="hljs-keyword">operator</span>+(MInt lhs, MInt rhs) {<br> MInt res = lhs;<br> res += rhs;<br> <span class="hljs-keyword">return</span> res;<br> }<br> <span class="hljs-keyword">friend</span> <span class="hljs-keyword">constexpr</span> MInt <span class="hljs-keyword">operator</span>-(MInt lhs, MInt rhs) {<br> MInt res = lhs;<br> res -= rhs;<br> <span class="hljs-keyword">return</span> res;<br> }<br> <span class="hljs-keyword">friend</span> <span class="hljs-keyword">constexpr</span> MInt <span class="hljs-keyword">operator</span>/(MInt lhs, MInt rhs) {<br> MInt res = lhs;<br> res /= rhs;<br> <span class="hljs-keyword">return</span> res;<br> }<br> <span class="hljs-keyword">friend</span> <span class="hljs-keyword">constexpr</span> std::istream &<span class="hljs-keyword">operator</span>>>(std::istream &is, MInt &a) {<br> i64 v;<br> is >> v;<br> a = <span class="hljs-built_in">MInt</span>(v);<br> <span class="hljs-keyword">return</span> is;<br> }<br> <span class="hljs-keyword">friend</span> <span class="hljs-keyword">constexpr</span> std::ostream &<span class="hljs-keyword">operator</span><<(std::ostream &os, <span class="hljs-type">const</span> MInt &a) {<br> <span class="hljs-keyword">return</span> os << a.<span class="hljs-built_in">val</span>();<br> }<br> <span class="hljs-keyword">friend</span> <span class="hljs-keyword">constexpr</span> <span class="hljs-type">bool</span> <span class="hljs-keyword">operator</span>==(MInt lhs, MInt rhs) {<br> <span class="hljs-keyword">return</span> lhs.<span class="hljs-built_in">val</span>() == rhs.<span class="hljs-built_in">val</span>();<br> }<br> <span class="hljs-keyword">friend</span> <span class="hljs-keyword">constexpr</span> <span class="hljs-type">bool</span> <span class="hljs-keyword">operator</span>!=(MInt lhs, MInt rhs) {<br> <span class="hljs-keyword">return</span> lhs.<span class="hljs-built_in">val</span>() != rhs.<span class="hljs-built_in">val</span>();<br> }<br>};<br><br><span class="hljs-keyword">template</span><><br><span class="hljs-type">int</span> MInt<<span class="hljs-number">0</span>>::Mod = <span class="hljs-number">998244353</span>;<br><br><span class="hljs-keyword">template</span><<span class="hljs-type">int</span> V, <span class="hljs-type">int</span> P><br><span class="hljs-keyword">constexpr</span> MInt<P> CInv = <span class="hljs-built_in">MInt</span><P>(V).<span class="hljs-built_in">inv</span>();<br><br><span class="hljs-keyword">constexpr</span> <span class="hljs-type">int</span> P = <span class="hljs-number">998244353</span>;<br><span class="hljs-keyword">using</span> Z = MInt<P>;<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{<br> std::ios::<span class="hljs-built_in">sync_with_stdio</span>(<span class="hljs-literal">false</span>);<br> std::cin.<span class="hljs-built_in">tie</span>(<span class="hljs-literal">nullptr</span>);<br> <br> i64 N;<br> std::cin >> N;<br> N++;<br> <br> <span class="hljs-type">int</span> A1, A2, A3;<br> std::cin >> A1 >> A2 >> A3;<br> <br> Z ans = <span class="hljs-number">0</span>;<br> Z dp[<span class="hljs-number">2</span>][<span class="hljs-number">2</span>][<span class="hljs-number">2</span>][<span class="hljs-number">10</span>][<span class="hljs-number">10</span>][<span class="hljs-number">10</span>];<br> dp[<span class="hljs-number">0</span>][<span class="hljs-number">0</span>][<span class="hljs-number">0</span>][<span class="hljs-number">0</span>][<span class="hljs-number">0</span>][<span class="hljs-number">0</span>] = <span class="hljs-number">1</span>;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> t = <span class="hljs-number">59</span>; t >= <span class="hljs-number">0</span>; t--) {<br> <span class="hljs-type">int</span> n = N >> t & <span class="hljs-number">1</span>;<br> Z g[<span class="hljs-number">2</span>][<span class="hljs-number">2</span>][<span class="hljs-number">2</span>][<span class="hljs-number">10</span>][<span class="hljs-number">10</span>][<span class="hljs-number">10</span>];<br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span> l1 : {<span class="hljs-number">0</span>, <span class="hljs-number">1</span>}) {<br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span> l2 : {<span class="hljs-number">0</span>, <span class="hljs-number">1</span>}) {<br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span> l3 : {<span class="hljs-number">0</span>, <span class="hljs-number">1</span>}) {<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> m1 = <span class="hljs-number">0</span>; m1 < A1; m1++) {<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> m2 = <span class="hljs-number">0</span>; m2 < A2; m2++) {<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> m3 = <span class="hljs-number">0</span>; m3 < A3; m3++) {<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> x1 = <span class="hljs-number">0</span>; x1 <= (l1 ? <span class="hljs-number">1</span> : n); x1++) {<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> x2 = <span class="hljs-number">0</span>; x2 <= (l2 ? <span class="hljs-number">1</span> : n); x2++) {<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> x3 = <span class="hljs-number">0</span>; x3 <= (l3 ? <span class="hljs-number">1</span> : n); x3++) {<br> <span class="hljs-keyword">if</span> ((x1 ^ x2 ^ x3) == <span class="hljs-number">0</span>) {<br> g[l1 || x1 < n][l2 || x2 < n][l3 || x3 < n][(m1 * <span class="hljs-number">2</span> + x1) % A1][(m2 * <span class="hljs-number">2</span> + x2) % A2][(m3 * <span class="hljs-number">2</span> + x3) % A3]<br> += dp[l1][l2][l3][m1][m2][m3];<br> }<br> }<br> }<br> }<br> }<br> }<br> }<br> }<br> }<br> }<br> std::<span class="hljs-built_in">swap</span>(dp, g);<br> }<br> <br> ans = dp[<span class="hljs-number">1</span>][<span class="hljs-number">1</span>][<span class="hljs-number">1</span>][<span class="hljs-number">0</span>][<span class="hljs-number">0</span>][<span class="hljs-number">0</span>];<br> N--;<br> ans -= N / std::<span class="hljs-built_in">lcm</span>(A1, A2);<br> ans -= N / std::<span class="hljs-built_in">lcm</span>(A1, A3);<br> ans -= N / std::<span class="hljs-built_in">lcm</span>(A2, A3);<br> ans -= <span class="hljs-number">1</span>;<br> std::cout << ans << <span class="hljs-string">"\n"</span>;<br> <br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></details><p><br></p><hr /><h2 id="g---rearranging-abc317-g"><ahref="https://atcoder.jp/contests/abc317/tasks/abc317_g">G - Rearranging(abc317 G)</a></h2><h3 id="题目大意-6">题目大意</h3><p><++></p><h3 id="解题思路-6">解题思路</h3><p>看题解是什么二分图完美匹配+最大流。先学学网络流再来补</p><details><summary>神奇的代码</summary><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><br></code></pre></td></tr></table></figure></details><p><br></p><hr /><h2 id="ex---walk-abc317-ex"><ahref="https://atcoder.jp/contests/abc317/tasks/abc317_h">Ex - Walk(abc317 Ex)</a></h2><h3 id="题目大意-7">题目大意</h3><p><++></p><h3 id="解题思路-7">解题思路</h3><p><++></p><details><summary>神奇的代码</summary><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><br></code></pre></td></tr></table></figure></details><p><br></p><hr />]]></content>
<categories>
<category>算法题解</category>
</categories>
<tags>
<tag>atcoder</tag>
</tags>
</entry>
<entry>
<title>Codeforces Round 894 (Div. 3)</title>
<link href="/2023/08/30/Codeforces%20Round%20894%20(Div.%203)/"/>
<url>/2023/08/30/Codeforces%20Round%20894%20(Div.%203)/</url>
<content type="html"><![CDATA[<h2 id="a---gift-carpet-cf1862-a"><ahref="https://codeforces.com/contest/1862/problem/A">A - Gift Carpet(CF1862 A)</a></h2><h3 id="题目大意">题目大意</h3><p>看是不是能拿出四列,按顺序出现"vika"四个字母。</p><h3 id="解题思路">解题思路</h3><p>不解释</p><details><summary>神奇的代码</summary><figure class="highlight cpp"><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><bits/stdc++.h></span></span><br><br><span class="hljs-keyword">using</span> i64 = <span class="hljs-type">long</span> <span class="hljs-type">long</span>;<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">solve</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-type">int</span> n, m;<br> std::cin >> n >> m;<br><br> std::vector<std::vector<<span class="hljs-type">char</span>>> <span class="hljs-built_in">a</span>(n + <span class="hljs-number">1</span>, std::<span class="hljs-built_in">vector</span><<span class="hljs-type">char</span>>(m + <span class="hljs-number">1</span>));<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">1</span>;i <= n;i ++) {<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> j = <span class="hljs-number">1</span>;j <= m;j ++) {<br> std::cin >> a[i][j];<br> }<br> }<br><br> std::string res = <span class="hljs-string">"#vika"</span>;<br> <span class="hljs-type">int</span> pos = <span class="hljs-number">1</span>;<br><br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> j = <span class="hljs-number">1</span>;j <= m;j ++) {<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">1</span>;i <= n;i ++) {<br> <span class="hljs-keyword">if</span> (a[i][j] == res[pos]) {<br> pos ++;<br> <span class="hljs-keyword">break</span>;<br> }<br> }<br> }<br><br> <span class="hljs-keyword">if</span> (pos == <span class="hljs-number">5</span>) {<br> std::cout << <span class="hljs-string">"YES"</span> << std::endl;<br> } <span class="hljs-keyword">else</span> {<br> std::cout << <span class="hljs-string">"NO"</span> << std::endl;<br> }<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{<br> std::ios::<span class="hljs-built_in">sync_with_stdio</span>(<span class="hljs-literal">false</span>);<br> std::cin.<span class="hljs-built_in">tie</span>(<span class="hljs-literal">nullptr</span>);<br><br> <span class="hljs-type">int</span> tt = <span class="hljs-number">1</span>;<br> std::cin >> tt;<br> <span class="hljs-keyword">while</span> (tt --) <span class="hljs-built_in">solve</span>();<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></details><p><br></p><hr /><h2 id="b---sequence-game-cf1862-b"><ahref="https://codeforces.com/contest/1862/problem/B">B - Sequence Game(CF1862 B)</a></h2><h3 id="题目大意-1">题目大意</h3><h3 id="解题思路-1">解题思路</h3><p>如果一段区间是递增的,就已经符合题意,不用动。如果不是递增的,在前面复制一份就形成递增了。</p><details><summary>神奇的代码</summary><figure class="highlight cpp"><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><bits/stdc++.h></span></span><br><br><span class="hljs-keyword">using</span> i64 = <span class="hljs-type">long</span> <span class="hljs-type">long</span>;<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">solve</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-type">int</span> n;<br> std::cin >> n;<br><br> <span class="hljs-function">std::vector<<span class="hljs-type">int</span>> <span class="hljs-title">a</span><span class="hljs-params">(n)</span></span>;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>;i < n;i ++) {<br> std::cin >> a[i];<br> }<br><br> std::vector<<span class="hljs-type">int</span>> b;<br> b.<span class="hljs-built_in">emplace_back</span>(a.<span class="hljs-built_in">front</span>());<br><br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">1</span>;i < n;i ++) {<br> <span class="hljs-keyword">if</span> (a[i - <span class="hljs-number">1</span>] > a[i]) b.<span class="hljs-built_in">emplace_back</span>(a[i]); <br> b.<span class="hljs-built_in">emplace_back</span>(a[i]);<br> }<br><br> std::cout << b.<span class="hljs-built_in">size</span>() << std::endl;<br><br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> &item : b) {<br> std::cout << item << <span class="hljs-string">' '</span>;<br> }<br><br> std::cout << <span class="hljs-string">'\n'</span>;<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{<br> std::ios::<span class="hljs-built_in">sync_with_stdio</span>(<span class="hljs-literal">false</span>);<br> std::cin.<span class="hljs-built_in">tie</span>(<span class="hljs-literal">nullptr</span>);<br><br> <span class="hljs-type">int</span> tt = <span class="hljs-number">1</span>;<br> std::cin >> tt;<br> <span class="hljs-keyword">while</span> (tt --) <span class="hljs-built_in">solve</span>();<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></details><p><br></p><hr /><h2 id="c---flower-city-fence-cf1862-c"><ahref="https://codeforces.com/contest/1862/problem/C">C - Flower CityFence (CF1862 C)</a></h2><h3 id="题目大意-2">题目大意</h3><p><++></p><h3 id="解题思路-2">解题思路</h3><p>树状数组维护一下每一列的方块数,然后和原数组比较就行。</p><details><summary>神奇的代码</summary><figure class="highlight cpp"><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><bits/stdc++.h></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><cstddef></span></span><br><br><span class="hljs-keyword">using</span> i64 = <span class="hljs-type">long</span> <span class="hljs-type">long</span>;<br><br><span class="hljs-keyword">template</span><<span class="hljs-keyword">typename</span> T><br><span class="hljs-keyword">struct</span> <span class="hljs-title class_">FenwickTree</span>{<br> <span class="hljs-type">int</span> n;<br> std::vector<T> bit;<br><br> <span class="hljs-comment">// constructors</span><br> <span class="hljs-built_in">FenwickTree</span>(<span class="hljs-type">int</span> _n) : <span class="hljs-built_in">n</span>(_n), <span class="hljs-built_in">bit</span>(_n + <span class="hljs-number">1</span>) {}<br> <span class="hljs-built_in">FenwickTree</span>(std::vector<T> &a) : <span class="hljs-built_in">FenwickTree</span>(a.<span class="hljs-built_in">size</span>()){<br> <span class="hljs-keyword">for</span>(<span class="hljs-type">size_t</span> i = <span class="hljs-number">1</span>;i <= a.<span class="hljs-built_in">size</span>();i ++)<br> <span class="hljs-built_in">update</span>(i, a[i]);<br> }<br><br> <span class="hljs-comment">// Operations</span><br> <span class="hljs-function">T <span class="hljs-title">query</span><span class="hljs-params">(<span class="hljs-type">int</span> idx)</span></span>{<br> T res = <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> i = idx;i != <span class="hljs-number">0</span>; i -= i & -i)<br> res += bit[i];<br> <span class="hljs-keyword">return</span> res;<br> }<br><br> <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">update</span><span class="hljs-params">(<span class="hljs-type">int</span> idx, <span class="hljs-type">int</span> delta)</span></span>{<br> <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> i = idx;i <= n;i += i & -i)<br> bit[i] += delta;<br> }<br><br> <span class="hljs-function">T <span class="hljs-title">range_quary</span><span class="hljs-params">(<span class="hljs-type">int</span> l, <span class="hljs-type">int</span> r)</span></span>{<br> <span class="hljs-keyword">return</span> <span class="hljs-built_in">query</span>(r) - <span class="hljs-built_in">query</span>(l - <span class="hljs-number">1</span>);<br> }<br><br> <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">range_update</span><span class="hljs-params">(<span class="hljs-type">int</span> l, <span class="hljs-type">int</span> r, <span class="hljs-type">int</span> delta)</span></span>{<br> <span class="hljs-built_in">update</span>(l, delta);<br> <span class="hljs-built_in">update</span>(r + <span class="hljs-number">1</span>, -delta);<br> }<br>};<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">solve</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-type">int</span> n;<br> std::cin >> n;<br><br> <span class="hljs-function">std::vector<<span class="hljs-type">int</span>> <span class="hljs-title">a</span><span class="hljs-params">(n + <span class="hljs-number">1</span>)</span></span>;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">1</span>;i <= n;i ++) {<br> std::cin >> a[i];<br> }<br><br> <span class="hljs-keyword">if</span> (a[<span class="hljs-number">1</span>] > n) {<br> std::cout << <span class="hljs-string">"NO"</span> << <span class="hljs-string">'\n'</span>;<br> <span class="hljs-keyword">return</span> ;<br> }<br><br> <span class="hljs-function">FenwickTree<i64> <span class="hljs-title">bit</span><span class="hljs-params">(n)</span></span>;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">1</span>;i <= n;i ++) {<br> bit.<span class="hljs-built_in">range_update</span>(<span class="hljs-number">1</span>, a[i], <span class="hljs-number">1</span>);<br> }<br><br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">1</span>;i <= n;i ++) {<br> i64 res = bit.<span class="hljs-built_in">query</span>(i);<br> <span class="hljs-keyword">if</span> (res != a[i]) {<br> std::cout << <span class="hljs-string">"NO"</span> << <span class="hljs-string">'\n'</span>;<br> <span class="hljs-keyword">return</span> ;<br> }<br> }<br><br> std::cout << <span class="hljs-string">"YES"</span> << <span class="hljs-string">'\n'</span>;<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{<br> std::ios::<span class="hljs-built_in">sync_with_stdio</span>(<span class="hljs-literal">false</span>);<br> std::cin.<span class="hljs-built_in">tie</span>(<span class="hljs-literal">nullptr</span>);<br><br> <span class="hljs-type">int</span> tt = <span class="hljs-number">1</span>;<br> std::cin >> tt;<br> <span class="hljs-keyword">while</span> (tt --) <span class="hljs-built_in">solve</span>();<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></details><p><br></p><hr /><h2 id="d---ice-cream-balls-cf1862-d"><ahref="https://codeforces.com/contest/1862/problem/D">D - Ice Cream Balls(CF1862 D)</a></h2><h3 id="题目大意-3">题目大意</h3><p><++></p><h3 id="解题思路-3">解题思路</h3><p>首先要最少,肯定所有数字不一样的时候最少。可以先二分来找到刚好<spanclass="math inline">\(C_i^2\)</span>小于n的<spanclass="math inline">\(i\)</span>。 然后考虑要添加到恰好n份,只需要添加$n - C_i^i $个相同的数字即可。</p><details><summary>神奇的代码</summary><figure class="highlight cpp"><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><bits/stdc++.h></span></span><br><br><span class="hljs-keyword">using</span> i64 = <span class="hljs-type">long</span> <span class="hljs-type">long</span>;<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">solve</span><span class="hljs-params">()</span> </span>{<br> i64 n;<br> std::cin >> n;<br><br> <span class="hljs-keyword">auto</span> check = [&](i64 x) {<br> i64 res = x * (x - <span class="hljs-number">1</span>) / <span class="hljs-number">2</span>;<br> <span class="hljs-keyword">return</span> res >= n;<br> };<br><br> i64 l = <span class="hljs-number">2</span>, r = <span class="hljs-number">2648956421</span>;<br> <span class="hljs-keyword">while</span> (l < r) {<br> <span class="hljs-type">int</span> mid = (l + r) >> <span class="hljs-number">1</span>;<br> <span class="hljs-keyword">if</span> (<span class="hljs-built_in">check</span>(mid)) r = mid;<br> <span class="hljs-keyword">else</span> l = mid + <span class="hljs-number">1</span>;<br> }<br><br> i64 res = l * (l - <span class="hljs-number">1</span>) / <span class="hljs-number">2</span>;<br> <span class="hljs-keyword">if</span> (res == n) {<br> std::cout << l << <span class="hljs-string">'\n'</span>;<br> <span class="hljs-keyword">return</span> ;<br> }<br><br> <span class="hljs-keyword">if</span> (res > n) {<br> res = (l - <span class="hljs-number">1</span>) * (l - <span class="hljs-number">2</span>) / <span class="hljs-number">2</span>;<br> std::cout << n - res + l - <span class="hljs-number">1</span> << <span class="hljs-string">'\n'</span>;<br> }<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{<br> std::ios::<span class="hljs-built_in">sync_with_stdio</span>(<span class="hljs-literal">false</span>);<br> std::cin.<span class="hljs-built_in">tie</span>(<span class="hljs-literal">nullptr</span>);<br><br> <span class="hljs-type">int</span> tt = <span class="hljs-number">1</span>;<br> std::cin >> tt;<br> <span class="hljs-keyword">while</span> (tt --) <span class="hljs-built_in">solve</span>();<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></details><p><br></p><hr /><h2 id="e---kolya-and-movie-theatre-cf1862-e"><ahref="https://codeforces.com/contest/1862/problem/E">E - Kolya and MovieTheatre (CF1862 E)</a></h2><h3 id="题目大意-4">题目大意</h3><p><++></p><h3 id="解题思路-4">解题思路</h3><p>注意到最后一场电影在第<spanclass="math inline">\(i\)</span>天看,则少的舒适度一定为<spanclass="math inline">\(d * i\)</span>。所以只需要求出前<spanclass="math inline">\(m\)</span>大的数的和即可。 枚举<spanclass="math inline">\(i\)</span>, 维护以下<spanclass="math inline">\(1~i\)</span>中的前<spanclass="math inline">\(m\)</span>大数的和,最后求出最大值即可。</p><details><summary>神奇的代码</summary><figure class="highlight cpp"><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><bits/stdc++.h></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><set></span></span><br><br><span class="hljs-keyword">using</span> i64 = <span class="hljs-type">long</span> <span class="hljs-type">long</span>;<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">solve</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-type">int</span> n, m, d;<br> std::cin >> n >> m >> d;<br><br> <span class="hljs-function">std::vector<<span class="hljs-type">int</span>> <span class="hljs-title">a</span><span class="hljs-params">(n + <span class="hljs-number">1</span>)</span></span>;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">1</span>;i <= n;i ++) {<br> std::cin >> a[i];<br> }<br><br> i64 ans = <span class="hljs-number">0</span>, sum = <span class="hljs-number">0</span>;<br> std::multiset<<span class="hljs-type">int</span>> st;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">1</span>;i <= n;i ++) {<br> <span class="hljs-keyword">if</span> (a[i] <= <span class="hljs-number">0</span>) <span class="hljs-keyword">continue</span>;<br> st.<span class="hljs-built_in">insert</span>(a[i]);<br> sum += a[i];<br> <span class="hljs-keyword">if</span> (st.<span class="hljs-built_in">size</span>() > m) {<br> sum -= *st.<span class="hljs-built_in">begin</span>();<br> st.<span class="hljs-built_in">erase</span>(st.<span class="hljs-built_in">begin</span>());<br> }<br> ans = std::<span class="hljs-built_in">max</span>(ans, sum - <span class="hljs-number">1LL</span> * i * d);<br> }<br><br> std::cout << ans << <span class="hljs-string">'\n'</span>;<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{<br> std::ios::<span class="hljs-built_in">sync_with_stdio</span>(<span class="hljs-literal">false</span>);<br> std::cin.<span class="hljs-built_in">tie</span>(<span class="hljs-literal">nullptr</span>);<br><br> <span class="hljs-type">int</span> tt = <span class="hljs-number">1</span>;<br> std::cin >> tt;<br> <span class="hljs-keyword">while</span> (tt --) <span class="hljs-built_in">solve</span>();<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></details><p><br></p><hr /><h2 id="f---magic-will-save-the-world-cf1862-f"><ahref="https://codeforces.com/contest/1862/problem/F">F - Magic Will Savethe World (CF1862 F)</a></h2><h3 id="题目大意-5">题目大意</h3><p><++></p><h3 id="解题思路-5">解题思路</h3><p>假设所有怪兽的总血量为<spanclass="math inline">\(s\)</span>,其中有<spanclass="math inline">\(x\)</span>滴血被水解决,剩下<spanclass="math inline">\(s - x\)</span>血量用火解决。则答案为<spanclass="math inline">\(max(\frac{x}{w}, \frac{s - x}{f})\)</span>。用<spanclass="math inline">\(0/1\)</span>背包预处理才出所有能取到的怪兽血量的集合,然后枚举更新答案即可。</p><details><summary>神奇的代码</summary><figure class="highlight cpp"><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><bits/stdc++.h></span></span><br> <br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br> <br><span class="hljs-meta">#<span class="hljs-keyword">define</span> int long long</span><br> <br><span class="hljs-function"><span class="hljs-type">int32_t</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-type">int</span> q;<br> cin >> q;<br> <span class="hljs-keyword">while</span> (q--) {<br> <span class="hljs-type">int</span> w, f, n;<br> cin >> w >> f >> n;<br> <span class="hljs-function">vector<<span class="hljs-type">int</span>> <span class="hljs-title">s</span><span class="hljs-params">(n)</span></span>;<br> <span class="hljs-type">int</span> sum_s = <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < n; ++i) {<br> cin >> s[i];<br> sum_s += s[i];<br> }<br> <span class="hljs-function">vector<<span class="hljs-type">bool</span>> <span class="hljs-title">dp</span><span class="hljs-params">(sum_s + <span class="hljs-number">1</span>)</span></span>;<br> dp[<span class="hljs-number">0</span>] = <span class="hljs-literal">true</span>;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < n; ++i) {<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> w = sum_s; w - s[i] >= <span class="hljs-number">0</span>; --w) {<br> dp[w] = dp[w] || dp[w - s[i]];<br> }<br> }<br> <span class="hljs-type">int</span> ans = <span class="hljs-number">2e9</span>;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i <= sum_s; ++i) {<br> <span class="hljs-keyword">if</span> (dp[i]) {<br> ans = <span class="hljs-built_in">min</span>(ans, <span class="hljs-built_in">max</span>((i + w - <span class="hljs-number">1</span>) / w, (sum_s - i + f - <span class="hljs-number">1</span>) / f));<br> }<br> }<br> cout << ans << <span class="hljs-string">"\n"</span>;<br> }<br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></details><p><br></p><hr /><h2 id="g---the-great-equalizer-cf1862-g"><ahref="https://codeforces.com/contest/1862/problem/G">G - The GreatEqualizer (CF1862 G)</a></h2><h3 id="题目大意-6">题目大意</h3><p><++></p><h3 id="解题思路-6">解题思路</h3><p>设<span class="math inline">\(d\)</span>数组为原数组数组排序后的差,猜的结论:<span class="math inline">\(ans = max(a) + max(d)\)</span></p><details><summary>神奇的代码</summary><figure class="highlight cpp"><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><bits/stdc++.h></span></span><br><br><span class="hljs-keyword">using</span> i64 = <span class="hljs-type">long</span> <span class="hljs-type">long</span>;<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">solve</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-type">int</span> n;<br> std::cin >> n;<br> <br> <span class="hljs-function">std::vector<<span class="hljs-type">int</span>> <span class="hljs-title">a</span><span class="hljs-params">(n)</span></span>;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < n; i++) {<br> std::cin >> a[i];<br> }<br> <br> std::multiset<<span class="hljs-type">int</span>> s, d{<span class="hljs-number">0</span>};<br> <br> <span class="hljs-keyword">auto</span> add = [&](<span class="hljs-type">int</span> x) {<br> <span class="hljs-keyword">auto</span> it = s.<span class="hljs-built_in">insert</span>(x);<br> <span class="hljs-keyword">auto</span> r = std::<span class="hljs-built_in">next</span>(it);<br> <span class="hljs-keyword">if</span> (it != s.<span class="hljs-built_in">begin</span>()) {<br> d.<span class="hljs-built_in">insert</span>(x - *std::<span class="hljs-built_in">prev</span>(it));<br> }<br> <span class="hljs-keyword">if</span> (r != s.<span class="hljs-built_in">end</span>()) {<br> d.<span class="hljs-built_in">insert</span>(*r - x);<br> }<br> <span class="hljs-keyword">if</span> (it != s.<span class="hljs-built_in">begin</span>() && r != s.<span class="hljs-built_in">end</span>()) {<br> d.<span class="hljs-built_in">extract</span>(*r - *std::<span class="hljs-built_in">prev</span>(it));<br> }<br> };<br> <br> <span class="hljs-keyword">auto</span> del = [&](<span class="hljs-type">int</span> x) {<br> <span class="hljs-keyword">auto</span> it = s.<span class="hljs-built_in">find</span>(x);<br> <span class="hljs-keyword">auto</span> r = std::<span class="hljs-built_in">next</span>(it);<br> <span class="hljs-keyword">if</span> (it != s.<span class="hljs-built_in">begin</span>()) {<br> d.<span class="hljs-built_in">extract</span>(x - *std::<span class="hljs-built_in">prev</span>(it));<br> }<br> <span class="hljs-keyword">if</span> (r != s.<span class="hljs-built_in">end</span>()) {<br> d.<span class="hljs-built_in">extract</span>(*r - x);<br> }<br> <span class="hljs-keyword">if</span> (it != s.<span class="hljs-built_in">begin</span>() && r != s.<span class="hljs-built_in">end</span>()) {<br> d.<span class="hljs-built_in">insert</span>(*r - *std::<span class="hljs-built_in">prev</span>(it));<br> }<br> s.<span class="hljs-built_in">erase</span>(it);<br> };<br> <br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < n; i++) {<br> <span class="hljs-built_in">add</span>(a[i]);<br> }<br> <br> <span class="hljs-type">int</span> q;<br> std::cin >> q;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < q; i++) {<br> <span class="hljs-type">int</span> x, y;<br> std::cin >> x >> y;<br> x--;<br> <span class="hljs-built_in">del</span>(a[x]);<br> a[x] = y;<br> <span class="hljs-built_in">add</span>(a[x]);<br> <span class="hljs-type">int</span> ans = *s.<span class="hljs-built_in">rbegin</span>() + *d.<span class="hljs-built_in">rbegin</span>();<br> std::cout << ans << <span class="hljs-string">" \n"</span>[i == q - <span class="hljs-number">1</span>];<br> }<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{<br> std::ios::<span class="hljs-built_in">sync_with_stdio</span>(<span class="hljs-literal">false</span>);<br> std::cin.<span class="hljs-built_in">tie</span>(<span class="hljs-literal">nullptr</span>);<br> <br> <span class="hljs-type">int</span> t;<br> std::cin >> t;<br> <br> <span class="hljs-keyword">while</span> (t--) {<br> <span class="hljs-built_in">solve</span>();<br> }<br> <br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br><br></code></pre></td></tr></table></figure></details><p><br></p><hr />]]></content>
<categories>
<category>算法题解</category>
</categories>
<tags>
<tag>codeforces</tag>
</tags>
</entry>
<entry>
<title>2023 SDNU ACM-ICPC集训队纳新通知</title>
<link href="/2023/08/29/2023%20SDNU%20ACM-ICPC%E9%9B%86%E8%AE%AD%E9%98%9F%E7%BA%B3%E6%96%B0%E9%80%9A%E7%9F%A5/"/>
<url>/2023/08/29/2023%20SDNU%20ACM-ICPC%E9%9B%86%E8%AE%AD%E9%98%9F%E7%BA%B3%E6%96%B0%E9%80%9A%E7%9F%A5/</url>
<content type="html"><![CDATA[<h1 id="sdnu-acm-icpc集训队纳新通知">2023 SDNUACM-ICPC集训队纳新通知</h1><p><img src="/img/2023纳新/title.png" /></p><h2 id="写在前面的话">写在前面的话</h2><p><img src="/img/2023纳新/前言.png" /></p><h2 id="acm是什么牛马">ACM是什么牛马?!</h2><p><strong>ACM国际大学生程序设计竞赛</strong>(英文全称:ACMInternational Collegiate ProgrammingContest(简称ACM-ICPC或ICPC))是由<strong>美国计算机协会(ACM)</strong>主办的,目前由AWS、华为和Jetbrains三家公司赞助,一项旨在展示大学生创新能力、团队精神和在压力下编写程序、分析和解决问题能力的年度竞赛。</p><p>以中国所在的<strong>东亚赛区</strong>为例,每年有5站左右的区域赛分站赛,在这里全国各个高校的大学生们同台竞技,<strong>三个人一台电脑在5小时</strong>的时间内通过<strong>编写程序</strong>来解决若干问题,各赛站脱颖而出的队伍即有获得参加世界总决赛(ICPCWorld Final)的机会。在这里,如果你有能力,哪里都是你的舞台。</p><p>经过40多年的发展,ACM国际大学生程序设计竞赛已经发展成为全球最具影响力的大学生程序设计竞赛。</p><p><img src="/img/2023纳新/2018WF.jpg" /></p><center><b><font size ='2'>2018年由北京大学举办的WF现场</font></b></center><p></font></p><h2 id="加入acm集训队后">加入ACM集训队后</h2><p><img src="/img/2023纳新/加入后.png" /></p><h2 id="我们的实验室">我们的实验室</h2><p>集训室在信工楼二楼224实验室.</p><p><img src="/img/2023纳新/实验室1.png" /></p><p><img src="/img/2023纳新/实验室2.png" /></p><h2 id="我们获得过的荣誉">我们获得过的荣誉</h2><p><img src="/img/2023纳新/gold1.png" /></p><p><img src="/img/2023纳新/gold2.png" /></p><p><img src="/img/2023纳新/gold3.png" /></p><p><img src="/img/2023纳新/sliver1.png" /></p><h2 id="我们毕业后的去处">我们毕业后的去处</h2><h4id="就业去向截止19级队员毕业后就职的公司有字节跳动快手美团小米百度小马智行拼多多安恒信息北京豪腾等国内知名it企业">就业去向:截止19级队员,毕业后就职的公司有字节跳动、快手、美团、小米、百度、小马智行、拼多多、安恒信息、北京豪腾等国内知名IT企业。</h4><h4id="保研去向截止20级队员保送的学校有中国科学院计算所中国科学院深圳所山东大学东北大学华中科技大学西北工业大学华东师范大学北京邮电大学北京交通大学等知名高校或科研院校攻读硕士或博士">保研去向:截止20级队员,保送的学校有:中国科学院计算所、中国科学院深圳所、山东大学、东北大学、华中科技大学、西北工业大学、华东师范大学、北京邮电大学、北京交通大学等知名高校或科研院校攻读硕士或博士。</h4><h2 id="如何加入我们">如何加入我们</h2><p>我们将会在新生军训结束后举行纳新宣讲会,大家可以到时候参加宣讲会了解更多内容。</p><p>目前有两种入队方法:一、宣讲会结束之后,我们会为所有有意愿加入我们的同学准备一份<strong>笔试</strong>题目(很简单的数学题,思维题(类似脑筋急转弯)。主要以思维题为主,高考内容涉及极少),然后我们将会对通过笔试筛选的同学进行<strong>面试</strong>,如果你通过了这两项考核,那么你就可以成为预备队员,再根据后期表现进行筛选。(方式一)</p><p>二、在<strong>Virtual judge(https://vjudge.net/article/4041)</strong>,完成所有指定题目的同学,可免去笔试,直接获得面试机会(每人至多一次)。关于Vjudge的使用方法,可以参考这个文章:</p><p><strong>无论你是小白还是大佬,有无编程基础,只要有耐心,有毅力,我们就欢迎你的加入</strong></p><p><strong>注:选拔的队员仅限于计算机科学与技术专业和人工智能专业的同学。由于通信专业的课程和比赛的内容有较大出入,也有其他更适合通信的比赛,所以暂定不接受通信专业的新生</strong></p><p>SDNUACM-ICPC新生交流群:<strong>857560238</strong>。有意向的同学可以先入群了解更多信息。</p><p><img src="/img/2023纳新/二维码.jpg" /></p>]]></content>
<categories>
<category>SDNU-ICPC</category>
</categories>
<tags>
<tag>SDNU-ICPC</tag>
</tags>
</entry>
<entry>
<title>Codeforces Round 893 (Div. 2)</title>
<link href="/2023/08/29/Codeforces%20Round%20893%20(Div.%202)/"/>
<url>/2023/08/29/Codeforces%20Round%20893%20(Div.%202)/</url>
<content type="html"><![CDATA[<h2 id="a---buttons-cf1858-a"><ahref="https://codeforces.com/contest/1858/problem/A">A - Buttons (CF1858A)</a></h2><h3 id="题目大意">题目大意</h3><p>anna和katie轮流按按钮,anna只能按按钮A和C,katie只能按下按钮B和C。不能按下按钮的一方输。求最后谁赢</p><h3 id="解题思路">解题思路</h3><p>根据峡谷经济学,有中立资源时先控中立资源。</p><details><summary>神奇的代码</summary><figure class="highlight cpp"><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><bits/stdc++.h></span></span><br><br><span class="hljs-keyword">using</span> i64 = <span class="hljs-type">long</span> <span class="hljs-type">long</span>;<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">solve</span><span class="hljs-params">()</span> </span>{<br> i64 a, b, c;<br> std::cin >> a >> b >> c;<br><br> i64 tmp = (c + <span class="hljs-number">1</span>) >> <span class="hljs-number">1</span>;<br> a += tmp; b += c - tmp;<br><br> <span class="hljs-keyword">if</span> (a > b) {<br> std::cout << <span class="hljs-string">"First"</span> << std::endl;<br> } <span class="hljs-keyword">else</span> {<br> std::cout << <span class="hljs-string">"Second"</span> << std::endl;<br> }<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{<br> std::ios::<span class="hljs-built_in">sync_with_stdio</span>(<span class="hljs-literal">false</span>);<br> std::cin.<span class="hljs-built_in">tie</span>(<span class="hljs-literal">nullptr</span>);<br><br> <span class="hljs-type">int</span> tt = <span class="hljs-number">1</span>;<br> std::cin >> tt;<br> <span class="hljs-keyword">while</span> (tt --) <span class="hljs-built_in">solve</span>();<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></details><p><br></p><hr /><h2 id="b---the-walkway-cf1858-b"><ahref="https://codeforces.com/contest/1858/problem/B">B - The Walkway(CF1858 B)</a></h2><h3 id="题目大意-1">题目大意</h3><p>太长不写版</p><h3 id="解题思路-1">解题思路</h3><p>BC难度反了。注意到每次删掉椅子,贡献最多减一,枚举减掉哪个就行。提前存储下吃饼干的点可以化简复杂度。</p><details><summary>神奇的代码</summary><figure class="highlight cpp"><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><bits/stdc++.h></span></span><br><br><span class="hljs-keyword">using</span> i64 = <span class="hljs-type">long</span> <span class="hljs-type">long</span>;<br><span class="hljs-keyword">using</span> PII = std::pair<<span class="hljs-type">int</span>, <span class="hljs-type">int</span>>;<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">solve</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-type">int</span> n, m, d;<br> std::cin >> n >> m >> d;<br><br> <span class="hljs-function">std::vector<<span class="hljs-type">int</span>> <span class="hljs-title">a</span><span class="hljs-params">(m + <span class="hljs-number">2</span>)</span></span>;<br> a[<span class="hljs-number">0</span>] = -d + <span class="hljs-number">1</span>;<br> a[m + <span class="hljs-number">1</span>] = n + <span class="hljs-number">1</span>;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">1</span>;i <= m;i ++) {<br> std::cin >> a[i];<br> }<br><br> <span class="hljs-type">int</span> cnt = <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">1</span>;i <= m + <span class="hljs-number">1</span>;i ++) {<br> cnt += (a[i] - a[i - <span class="hljs-number">1</span>] - <span class="hljs-number">1</span>) / d;<br> }<br><br> <span class="hljs-type">int</span> res = (<span class="hljs-number">1</span> << <span class="hljs-number">30</span>), ans = <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">1</span>;i <= m;i ++) {<br> <span class="hljs-type">int</span> tmp = cnt;<br> tmp -= (a[i] - a[i - <span class="hljs-number">1</span>] - <span class="hljs-number">1</span>) / d;<br> tmp -= (a[i + <span class="hljs-number">1</span>] - a[i] - <span class="hljs-number">1</span>) / d;<br> tmp += (a[i + <span class="hljs-number">1</span>] - a[i - <span class="hljs-number">1</span>] - <span class="hljs-number">1</span>) / d;<br> tmp += m - <span class="hljs-number">1</span>;<br> <span class="hljs-keyword">if</span> (tmp < res) {<br> res = tmp;<br> ans = <span class="hljs-number">1</span>;<br> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (res == tmp) {<br> ans ++;<br> }<br> }<br><br> std::cout << res << <span class="hljs-string">' '</span> << ans << std::endl;<br> <br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{<br> std::ios::<span class="hljs-built_in">sync_with_stdio</span>(<span class="hljs-literal">false</span>);<br> std::cin.<span class="hljs-built_in">tie</span>(<span class="hljs-literal">nullptr</span>);<br><br> <span class="hljs-type">int</span> tt = <span class="hljs-number">1</span>;<br> std::cin >> tt;<br> <span class="hljs-keyword">while</span> (tt --) <span class="hljs-built_in">solve</span>();<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></details><p><br></p><hr /><h2 id="c---yet-another-permutation-problem-cf1858-c"><ahref="https://codeforces.com/contest/1858/problem/C">C - Yet AnotherPermutation Problem (CF1858 C)</a></h2><h3 id="题目大意-2">题目大意</h3><p>找到一个排列,使得相邻两个数的gcd的不同个数最多</p><h3 id="解题思路-2">解题思路</h3><p>按倍数构造,一眼题。</p><details><summary>神奇的代码</summary><figure class="highlight cpp"><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><bits/stdc++.h></span></span><br><br><span class="hljs-keyword">using</span> i64 = <span class="hljs-type">long</span> <span class="hljs-type">long</span>;<br><span class="hljs-keyword">using</span> PII = std::pair<<span class="hljs-type">int</span>, <span class="hljs-type">int</span>>;<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">solve</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-type">int</span> n;<br> std::cin >> n;<br><br> std::cout << <span class="hljs-number">1</span> << <span class="hljs-string">' '</span>;<br><br> std::bitset<100010> vis;<br> vis[<span class="hljs-number">1</span>] = <span class="hljs-literal">true</span>;<br><br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">2</span>;i <= n;i ++) {<br> <span class="hljs-keyword">if</span> (vis[i]) <span class="hljs-keyword">continue</span>;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> j = i;j <= n;j += j) {<br> <span class="hljs-keyword">if</span> (!vis[j]) vis[j] = <span class="hljs-literal">true</span>;<br> std::cout << j << <span class="hljs-string">' '</span>;<br> }<br> }<br> <br> std::cout << std::endl;<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{<br> std::ios::<span class="hljs-built_in">sync_with_stdio</span>(<span class="hljs-literal">false</span>);<br> std::cin.<span class="hljs-built_in">tie</span>(<span class="hljs-literal">nullptr</span>);<br><br> <span class="hljs-type">int</span> tt = <span class="hljs-number">1</span>;<br> std::cin >> tt;<br> <span class="hljs-keyword">while</span> (tt --) <span class="hljs-built_in">solve</span>();<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></details><p><br></p><hr /><h2 id="d---trees-and-segments-cf1858-d"><ahref="https://codeforces.com/contest/1858/problem/D">D - Trees andSegments (CF1858 D)</a></h2><h3 id="题目大意-3">题目大意</h3><p><++></p><h3 id="解题思路-3">解题思路</h3><p><++></p><details><summary>神奇的代码</summary><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><br></code></pre></td></tr></table></figure></details><p><br></p><hr /><h2 id="e1---rollbacks-easy-version-cf1858-e1"><ahref="https://codeforces.com/contest/1858/problem/E1">E1 - Rollbacks(Easy Version) (CF1858 E1)</a></h2><h3 id="题目大意-4">题目大意</h3><p><++></p><h3 id="解题思路-4">解题思路</h3><p><++></p><details><summary>神奇的代码</summary><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><br></code></pre></td></tr></table></figure></details><p><br></p><hr /><h2 id="e2---rollbacks-hard-version-cf1858-e2"><ahref="https://codeforces.com/contest/1858/problem/E2">E2 - Rollbacks(Hard Version) (CF1858 E2)</a></h2><h3 id="题目大意-5">题目大意</h3><p><++></p><h3 id="解题思路-5">解题思路</h3><p><++></p><details><summary>神奇的代码</summary><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><br></code></pre></td></tr></table></figure></details><p><br></p><hr />]]></content>
<categories>
<category>算法题解</category>
</categories>
<tags>
<tag>codeforces</tag>
</tags>
</entry>
<entry>
<title>AtCoder Beginner Contest 315</title>
<link href="/2023/08/23/AtCoder%20Beginner%20Contest%20315/"/>
<url>/2023/08/23/AtCoder%20Beginner%20Contest%20315/</url>
<content type="html"><![CDATA[<h2 id="a---tcdr-abc315-a"><ahref="https://atcoder.jp/contests/abc315/tasks/abc315_a">A - tcdr(abc315 A)</a></h2><h3 id="题目大意">题目大意</h3><p>从给定的字符串中删除元音字母。</p><h3 id="解题思路">解题思路</h3><p>无</p><details><summary>神奇的代码</summary><figure class="highlight cpp"><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><bits/stdc++.h></span></span><br><br><span class="hljs-keyword">using</span> i64 = <span class="hljs-type">long</span> <span class="hljs-type">long</span>;<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{<br> std::ios::<span class="hljs-built_in">sync_with_stdio</span>(<span class="hljs-literal">false</span>);<br> std::cin.<span class="hljs-built_in">tie</span>(<span class="hljs-literal">nullptr</span>);<br><br> std::string s;<br> std::cin >> s;<br><br> std::string ans = {};<br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span> &item : s) {<br> <span class="hljs-keyword">if</span> (item == <span class="hljs-string">'a'</span> || item == <span class="hljs-string">'e'</span> || item == <span class="hljs-string">'i'</span> || item == <span class="hljs-string">'o'</span> || item == <span class="hljs-string">'u'</span>) <span class="hljs-keyword">continue</span>;<br> ans.<span class="hljs-built_in">push_back</span>(item);<br> }<br><br> std::cout << ans << <span class="hljs-string">'\n'</span>;<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></details><p><br></p><hr /><h2 id="b---the-middle-day-abc315-b"><ahref="https://atcoder.jp/contests/abc315/tasks/abc315_b">B - The MiddleDay (abc315 B)</a></h2><h3 id="题目大意-1">题目大意</h3><p>给定一年有<span class="math inline">\(n\)</span>个月,每个月有<spanclass="math inline">\(d_i\)</span>天,找一年中的中间一天是几月几日。</p><h3 id="解题思路-1">解题思路</h3><p>无</p><details><summary>神奇的代码</summary><figure class="highlight cpp"><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><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><bits/stdc++.h></span></span><br><br><span class="hljs-keyword">using</span> i64 = <span class="hljs-type">long</span> <span class="hljs-type">long</span>;<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{<br> std::ios::<span class="hljs-built_in">sync_with_stdio</span>(<span class="hljs-literal">false</span>);<br> std::cin.<span class="hljs-built_in">tie</span>(<span class="hljs-literal">nullptr</span>);<br><br> <span class="hljs-type">int</span> n;<br> std::cin >> n;<br><br> i64 sum = <span class="hljs-number">0</span>;<br> <span class="hljs-function">std::vector<<span class="hljs-type">int</span>> <span class="hljs-title">mouth</span><span class="hljs-params">(n + <span class="hljs-number">1</span>)</span></span>;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">1</span>;i <= n;i ++) {<br> std::cin >> mouth[i];<br> sum += mouth[i];<br> }<br><br> i64 mid = (sum + <span class="hljs-number">1</span>) / <span class="hljs-number">2</span>;<br><br> <span class="hljs-type">int</span> cur = <span class="hljs-number">1</span>;<br> <span class="hljs-keyword">while</span> (mid > <span class="hljs-number">0</span>) {<br> <span class="hljs-keyword">if</span> (mid - mouth[cur] <= <span class="hljs-number">0</span>) <span class="hljs-keyword">break</span>;<br> mid -= mouth[cur ++];<br> }<br><br> std::cout << cur << <span class="hljs-string">' '</span> << mid << std::endl;<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></details><p><br></p><hr /><h2 id="c---flavors-abc315-c"><ahref="https://atcoder.jp/contests/abc315/tasks/abc315_c">C - Flavors(abc315 C)</a></h2><h3 id="题目大意-2">题目大意</h3><p>有<span class="math inline">\(n\)</span>盘菜,种类和好吃度分别为<spanclass="math inline">\(f_i\)</span>和<spanclass="math inline">\(s_i\)</span>,从中选择两盘菜来吃。如果种类相同,则满意度为<spanclass="math inline">\(s + \frac{t}{2}\)</span>, 否则为<spanclass="math inline">\(s + t\)</span>。求出最大满意度。</p><h3 id="解题思路-2">解题思路</h3><p>枚举一下可能出现的情况就好。</p><p>首先枚举每个种类的菜好吃度最大的两盘的满意度。然后枚举不同种类中好吃度最大的两盘菜。最后输出最大值即可。</p><details><summary>神奇的代码</summary><figure class="highlight cpp"><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><algorithm></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><bits/stdc++.h></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><functional></span></span><br><br><span class="hljs-keyword">using</span> i64 = <span class="hljs-type">long</span> <span class="hljs-type">long</span>;<br><span class="hljs-keyword">using</span> PII = std::pair<<span class="hljs-type">int</span>, <span class="hljs-type">int</span>>;<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{<br> std::ios::<span class="hljs-built_in">sync_with_stdio</span>(<span class="hljs-literal">false</span>);<br> std::cin.<span class="hljs-built_in">tie</span>(<span class="hljs-literal">nullptr</span>);<br><br> <span class="hljs-type">int</span> n;<br> std::cin >> n;<br><br> std::vector<std::vector<<span class="hljs-type">int</span>>> <span class="hljs-built_in">a</span>(n + <span class="hljs-number">1</span>, std::<span class="hljs-built_in">vector</span><<span class="hljs-type">int</span>>());<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">1</span>;i <= n;i ++) {<br> <span class="hljs-type">int</span> x, y;<br> std::cin >> x >> y;<br> a[x].<span class="hljs-built_in">emplace_back</span>(y);<br> }<br><br> <span class="hljs-type">int</span> ans = <span class="hljs-number">0</span>;<br> <span class="hljs-function">std::vector<<span class="hljs-type">int</span>> <span class="hljs-title">max</span><span class="hljs-params">(n + <span class="hljs-number">1</span>)</span></span>;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">1</span>;i <= n;i ++) {<br> <span class="hljs-keyword">if</span> (a[i].<span class="hljs-built_in">empty</span>()) <span class="hljs-keyword">continue</span>;<br> std::<span class="hljs-built_in">nth_element</span>(a[i].<span class="hljs-built_in">begin</span>(), a[i].<span class="hljs-built_in">begin</span>()<span class="hljs-number">+1</span>, a[i].<span class="hljs-built_in">end</span>(), std::<span class="hljs-built_in">greater</span><<span class="hljs-type">int</span>>());<br> <span class="hljs-keyword">if</span> (a[i].<span class="hljs-built_in">size</span>() >= <span class="hljs-number">2</span>) {<br> ans = std::<span class="hljs-built_in">max</span>(ans, a[i][<span class="hljs-number">0</span>] + a[i][<span class="hljs-number">1</span>] / <span class="hljs-number">2</span>);<br> }<br> max[i] = a[i][<span class="hljs-number">0</span>];<br> }<br><br> std::<span class="hljs-built_in">nth_element</span>(max.<span class="hljs-built_in">begin</span>(), max.<span class="hljs-built_in">begin</span>() + <span class="hljs-number">1</span>, max.<span class="hljs-built_in">end</span>(), std::<span class="hljs-built_in">greater</span><<span class="hljs-type">int</span>>());<br> ans = std::<span class="hljs-built_in">max</span>(ans, max[<span class="hljs-number">0</span>] + max[<span class="hljs-number">1</span>]);<br> std::cout << ans << <span class="hljs-string">'\n'</span>;<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></details><p><br></p><hr /><h2 id="d---magical-cookies-abc315-d"><ahref="https://atcoder.jp/contests/abc315/tasks/abc315_d">D - MagicalCookies (abc315 D)</a></h2><h3 id="题目大意-3">题目大意</h3><p>在<span class="math inline">\(n*m\)</span>的网格中,每个格子有一块点心,种类为<spanclass="math inline">\(c_{ij}\)</span>,当一行或一列有两块以上相同种类的点心时,将他们都删去。求最后网格还剩多少块点心。</p><h3 id="解题思路-3">解题思路</h3><p>硬模拟。</p><details><summary>神奇的代码</summary><figure class="highlight cpp"><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><array></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><bits/stdc++.h></span></span><br><br><span class="hljs-keyword">using</span> i64 = <span class="hljs-type">long</span> <span class="hljs-type">long</span>;<br><span class="hljs-keyword">using</span> PII = std::pair<<span class="hljs-type">int</span>, <span class="hljs-type">int</span>>;<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{<br> std::ios::<span class="hljs-built_in">sync_with_stdio</span>(<span class="hljs-literal">false</span>);<br> std::cin.<span class="hljs-built_in">tie</span>(<span class="hljs-literal">nullptr</span>);<br><br> <span class="hljs-type">int</span> n, m;<br> std::cin >> n >> m;<br><br> std::vector<std::vector<<span class="hljs-type">char</span>>> <span class="hljs-built_in">a</span>(n + <span class="hljs-number">1</span>, std::<span class="hljs-built_in">vector</span><<span class="hljs-type">char</span>>(m + <span class="hljs-number">1</span>));<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">1</span>;i <= n;i ++) {<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> j = <span class="hljs-number">1</span>;j <= m;j ++) {<br> std::cin >> a[i][j];<br> }<br> }<br><br> std::vector<std::array<<span class="hljs-type">int</span>, 27>> <span class="hljs-built_in">cntr</span>(n + <span class="hljs-number">1</span>), <span class="hljs-built_in">cntc</span>(m + <span class="hljs-number">1</span>);<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">1</span>;i <= n;i ++) {<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> j = <span class="hljs-number">1</span>;j <= m;j ++) {<br> cntr[i][a[i][j] - <span class="hljs-string">'a'</span>] ++;<br> cntc[j][a[i][j] - <span class="hljs-string">'a'</span>] ++;<br> }<br> }<br><br> <span class="hljs-function">std::vector<<span class="hljs-type">bool</span>> <span class="hljs-title">visr</span><span class="hljs-params">(n + <span class="hljs-number">1</span>)</span>, <span class="hljs-title">visc</span><span class="hljs-params">(m + <span class="hljs-number">1</span>)</span></span>;<br> <span class="hljs-type">int</span> hc = n, wc = m;<br> <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> _ = <span class="hljs-number">1</span>; _ <= n + m; _++) {<br> std::vector<PII> tx, ty;<br> <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> i = <span class="hljs-number">1</span>; i <= n; i++) {<br> <span class="hljs-keyword">if</span>(visr[i]) <span class="hljs-keyword">continue</span>;<br> <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> j = <span class="hljs-number">0</span>; j < <span class="hljs-number">26</span>; j++) {<br> <span class="hljs-keyword">if</span>(cntr[i][j] == wc && wc > <span class="hljs-number">1</span>) {<br> tx.<span class="hljs-built_in">emplace_back</span>(i, j);<br> }<br> }<br> }<br><br> <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> i = <span class="hljs-number">1</span>; i <= m; i++) {<br> <span class="hljs-keyword">if</span>(visc[i]) <span class="hljs-keyword">continue</span>;<br> <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> j = <span class="hljs-number">0</span>; j < <span class="hljs-number">26</span>; j++) {<br> <span class="hljs-keyword">if</span>(cntc[i][j] == hc && hc > <span class="hljs-number">1</span>) {<br> ty.<span class="hljs-built_in">emplace_back</span>(i, j);<br> }<br> }<br> }<br><br> <span class="hljs-keyword">for</span>(<span class="hljs-keyword">auto</span> &[x, y] : tx) {<br> visr[x] = <span class="hljs-literal">true</span>;<br> <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> i = <span class="hljs-number">1</span>; i <= m; i++) {<br> cntc[i][y]--;<br> }<br> hc--;<br> }<br><br> <span class="hljs-keyword">for</span>(<span class="hljs-keyword">auto</span> &[x, y] : ty) {<br> visc[x] = <span class="hljs-literal">true</span>;<br> <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> i = <span class="hljs-number">1</span>; i <= n; i++) {<br> cntr[i][y]--;<br> }<br> wc--; <br> }<br> }<br><br> std::cout << wc * hc << <span class="hljs-string">"\n"</span>;<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></details><p><br></p><hr /><h2 id="e---prerequisites-abc315-e"><ahref="https://atcoder.jp/contests/abc315/tasks/abc315_e">E -Prerequisites (abc315 E)</a></h2><h3 id="题目大意-4">题目大意</h3><p>有<spanclass="math inline">\(n\)</span>本书,每本书需要先读给定的<spanclass="math inline">\(c_i\)</span>本书才能读。求读第一本书之前需要读的读书顺序。要求需要读的书籍最小。</p><h3 id="解题思路-4">解题思路</h3><p><spanclass="math inline">\(dfs\)</span>去找每本书需要的前置书籍,然后递归回来的顺序就是需要的顺序。好像拓扑排序也能写。</p><details><summary>神奇的代码</summary><figure class="highlight cpp"><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><algorithm></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><array></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><bits/stdc++.h></span></span><br><br><span class="hljs-keyword">using</span> i64 = <span class="hljs-type">long</span> <span class="hljs-type">long</span>;<br><span class="hljs-keyword">using</span> PII = std::pair<<span class="hljs-type">int</span>, <span class="hljs-type">int</span>>;<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{<br> std::ios::<span class="hljs-built_in">sync_with_stdio</span>(<span class="hljs-literal">false</span>);<br> std::cin.<span class="hljs-built_in">tie</span>(<span class="hljs-literal">nullptr</span>); <br><br> <span class="hljs-type">int</span> n;<br> std::cin >> n;<br> <br> std::vector<std::vector<<span class="hljs-type">int</span>>> <span class="hljs-built_in">g</span>(n + <span class="hljs-number">1</span>, std::<span class="hljs-built_in">vector</span><<span class="hljs-type">int</span>>());<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">1</span>;i <= n;i ++) {<br> <span class="hljs-type">int</span> c;<br> std::cin >> c;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> _ = <span class="hljs-number">1</span>;_ <= c;_ ++) {<br> <span class="hljs-type">int</span> x;<br> std::cin >> x;<br> g[i].<span class="hljs-built_in">emplace_back</span>(x);<br> }<br> }<br><br> <span class="hljs-function">std::vector<<span class="hljs-type">bool</span>> <span class="hljs-title">vis</span><span class="hljs-params">(n + <span class="hljs-number">1</span>)</span></span>;<br> std::function<<span class="hljs-type">void</span>(<span class="hljs-type">int</span>)> dfs = [&](<span class="hljs-type">int</span> u) -> <span class="hljs-type">void</span> {<br> <span class="hljs-keyword">if</span> (vis[u]) <span class="hljs-keyword">return</span> ;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> v : g[u]) {<br> <span class="hljs-built_in">dfs</span>(v);<br> }<br> vis[u] = <span class="hljs-literal">true</span>;<br> <span class="hljs-keyword">if</span> (u != <span class="hljs-number">1</span>) {<br> std::cout << u << <span class="hljs-string">' '</span>;<br> }<br> };<br><br> <span class="hljs-built_in">dfs</span>(<span class="hljs-number">1</span>);<br> std::cout << std::endl;<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></details><p><br></p><hr /><h2 id="f---shortcuts-abc315-f"><ahref="https://atcoder.jp/contests/abc315/tasks/abc315_f">F - Shortcuts(abc315 F)</a></h2><h3 id="题目大意-5">题目大意</h3><p>balabalabala好难概括。</p><h3 id="解题思路-5">解题思路</h3><p>设<span class="math inline">\(dp[i][j]\)</span>为走了前<spanclass="math inline">\(i\)</span>个点,已经跳过了<spanclass="math inline">\(j\)</span>个点,需要走的最小距离。</p><p>最大答案大约是<spanclass="math inline">\(1e4*1e4\)</span>,这时候跳过<spanclass="math inline">\(30\)</span>个点就大于了,所以只需要枚举跳过<spanclass="math inline">\(30\)</span>个点以内的情况。</p><details><summary>神奇的代码</summary><figure class="highlight cpp"><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></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><bits/stdc++.h></span></span><br><br><span class="hljs-keyword">using</span> i64 = <span class="hljs-type">long</span> <span class="hljs-type">long</span>;<br><br><span class="hljs-keyword">constexpr</span> <span class="hljs-type">int</span> K = <span class="hljs-number">30</span>;<br><span class="hljs-keyword">constexpr</span> <span class="hljs-type">double</span> inf = <span class="hljs-number">1E9</span>;<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{<br> std::ios::<span class="hljs-built_in">sync_with_stdio</span>(<span class="hljs-literal">false</span>);<br> std::cin.<span class="hljs-built_in">tie</span>(<span class="hljs-literal">nullptr</span>);<br> <br> <span class="hljs-type">int</span> N;<br> std::cin >> N;<br> <br> <span class="hljs-function">std::vector<<span class="hljs-type">int</span>> <span class="hljs-title">X</span><span class="hljs-params">(N)</span>, <span class="hljs-title">Y</span><span class="hljs-params">(N)</span></span>;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < N; i++) {<br> std::cin >> X[i] >> Y[i];<br> }<br> <br> std::vector<std::vector<<span class="hljs-type">double</span>>> <span class="hljs-built_in">dp</span>(N, std::<span class="hljs-built_in">vector</span><<span class="hljs-type">double</span>>(K, inf));<br> <span class="hljs-type">double</span> ans = inf;<br> dp[<span class="hljs-number">0</span>][<span class="hljs-number">0</span>] = <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < N - <span class="hljs-number">1</span>; i++) {<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> j = <span class="hljs-number">0</span>; j < K; j++) {<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> x = i + <span class="hljs-number">1</span>; x < N && j + x - i - <span class="hljs-number">1</span> < K; x++) {<br> <span class="hljs-type">int</span> nj = j + x - i - <span class="hljs-number">1</span>; <br> dp[x][nj] = std::<span class="hljs-built_in">min</span>(dp[x][nj], dp[i][j] + std::<span class="hljs-built_in">sqrt</span>((X[i] - X[x]) * (X[i] - X[x]) + (Y[i] - Y[x]) * (Y[i] - Y[x])));<br> }<br> }<br> }<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < K; i++) {<br> ans = std::<span class="hljs-built_in">min</span>(ans, dp[N - <span class="hljs-number">1</span>][i] + (i == <span class="hljs-number">0</span> ? <span class="hljs-number">0</span> : <span class="hljs-number">1</span> << (i - <span class="hljs-number">1</span>)));<br> }<br> std::cout << std::fixed << std::<span class="hljs-built_in">setprecision</span>(<span class="hljs-number">10</span>) << ans << <span class="hljs-string">"\n"</span>;<br> <br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br><br></code></pre></td></tr></table></figure></details><p><br></p><hr /><h2 id="g---ai-bj-ck-x-1-i-j-k-n-abc315-g"><ahref="https://atcoder.jp/contests/abc315/tasks/abc315_g">G - Ai + Bj +Ck = X (1 <= i, j, k <= N) (abc315 G)</a></h2><h3 id="题目大意-6">题目大意</h3><p><++></p><h3 id="解题思路-6">解题思路</h3><p><++></p><details><summary>神奇的代码</summary><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><br></code></pre></td></tr></table></figure></details><p><br></p><hr /><h2 id="ex---typical-convolution-problem-abc315-ex"><ahref="https://atcoder.jp/contests/abc315/tasks/abc315_h">Ex - TypicalConvolution Problem (abc315 Ex)</a></h2><h3 id="题目大意-7">题目大意</h3><p><++></p><h3 id="解题思路-7">解题思路</h3><p><++></p><details><summary>神奇的代码</summary><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><br></code></pre></td></tr></table></figure></details><p><br></p><hr />]]></content>
<categories>
<category>算法题解</category>
</categories>
<tags>
<tag>atcoder</tag>
</tags>
</entry>
</search>