-
Notifications
You must be signed in to change notification settings - Fork 0
/
content.json
1 lines (1 loc) · 181 KB
/
content.json
1
{"meta":{"title":"瓜子壳专用垃圾桶","subtitle":"Tech Otaku Save the World","description":"打铁$ ACMer $<br>主要发表$ ACM $相关,偶尔抽风 <br> ٩(๑>◡<๑)۶ <br> $ From GDUT $","author":"$ \\cal WiLe $","url":"http://WigginsLi.github.io"},"pages":[{"title":"","date":"2019-08-11T12:58:45.751Z","updated":"2019-06-17T12:57:41.966Z","comments":true,"path":"README.html","permalink":"http://WigginsLi.github.io/README.html","excerpt":"","text":"#This WiLe’s BlogQAQ"},{"title":"about","date":"2018-12-12T22:14:36.000Z","updated":"2019-08-09T10:37:57.928Z","comments":false,"path":"about/index.html","permalink":"http://WigginsLi.github.io/about/index.html","excerpt":"","text":"资料待补充(其实是我懒) update:19.06.21 勇者斗恶龙-$QAQ$->铁—>铜———–>银————-$DaLao$———>金——————————————>$FINAL$","keywords":"关于"},{"title":"bangumi","date":"2019-02-10T21:32:48.000Z","updated":"2019-08-11T12:53:40.090Z","comments":false,"path":"bangumi/index.html","permalink":"http://WigginsLi.github.io/bangumi/index.html","excerpt":"","text":"","keywords":null},{"title":"client","date":"2018-12-20T23:13:35.000Z","updated":"2019-05-31T23:21:46.000Z","comments":false,"path":"client/index.html","permalink":"http://WigginsLi.github.io/client/index.html","excerpt":"","text":"直接下载 or 扫码下载:","keywords":"Android客户端"},{"title":"comment","date":"2018-12-20T23:13:48.000Z","updated":"2019-05-31T23:21:46.000Z","comments":true,"path":"comment/index.html","permalink":"http://WigginsLi.github.io/comment/index.html","excerpt":"","text":"念两句诗 叙别梦、扬州一觉。 【宋代】吴文英《夜游宫·人去西楼雁杳》","keywords":"留言板"},{"title":"donate","date":"2018-12-20T23:13:05.000Z","updated":"2019-05-31T23:21:46.000Z","comments":false,"path":"donate/index.html","permalink":"http://WigginsLi.github.io/donate/index.html","excerpt":"","text":"","keywords":"谢谢饲主了喵~"},{"title":"lab","date":"2019-01-05T21:47:59.000Z","updated":"2019-05-31T23:21:46.000Z","comments":false,"path":"lab/index.html","permalink":"http://WigginsLi.github.io/lab/index.html","excerpt":"","text":"sakura主题balabala","keywords":"Lab实验室"},{"title":"music","date":"2018-12-20T23:14:28.000Z","updated":"2019-05-31T23:21:46.000Z","comments":false,"path":"music/index.html","permalink":"http://WigginsLi.github.io/music/index.html","excerpt":"","text":"","keywords":"喜欢的音乐"},{"title":"rss","date":"2018-12-20T23:09:03.000Z","updated":"2019-05-31T23:21:46.000Z","comments":true,"path":"rss/index.html","permalink":"http://WigginsLi.github.io/rss/index.html","excerpt":"","text":""},{"title":"links","date":"2018-12-19T23:11:06.000Z","updated":"2019-08-11T12:51:31.486Z","comments":true,"path":"links/index.html","permalink":"http://WigginsLi.github.io/links/index.html","excerpt":"","text":"","keywords":"友人帐"},{"title":"tags","date":"2018-12-12T22:14:16.000Z","updated":"2019-05-31T23:21:46.000Z","comments":true,"path":"tags/index.html","permalink":"http://WigginsLi.github.io/tags/index.html","excerpt":"","text":""},{"title":"theme-sakura","date":"2019-01-04T22:53:25.000Z","updated":"2019-05-31T23:21:46.000Z","comments":false,"path":"theme-sakura/index.html","permalink":"http://WigginsLi.github.io/theme-sakura/index.html","excerpt":"","text":"Hexo主题Sakura修改自WordPress主题Sakura,感谢原作者Mashiro","keywords":"Hexo 主题 Sakura 🌸"},{"title":"video","date":"2018-12-20T23:14:38.000Z","updated":"2019-05-31T23:21:46.000Z","comments":false,"path":"video/index.html","permalink":"http://WigginsLi.github.io/video/index.html","excerpt":"","text":"var videos = [ { img: 'https://lain.bgm.tv/pic/cover/l/0e/1e/218971_2y351.jpg', title: '朝花夕誓——于离别之朝束起约定之花', status: '已追完', progress: 100, jp: 'さよならの朝に約束の花をかざろう', time: '放送时间: 2018-02-24 SUN.', desc: ' 住在远离尘嚣的土地,一边将每天的事情编织成名为希比欧的布,一边静静生活的伊欧夫人民。在15岁左右外表就停止成长,拥有数百年寿命的他们,被称为“离别的一族”,并被视为活着的传说。没有双亲的伊欧夫少女玛奇亚,过着被伙伴包围的平稳日子,却总感觉“孤身一人”。他们的这种日常,一瞬间就崩溃消失。追求伊欧夫的长寿之血,梅萨蒂军乘坐着名为雷纳特的古代兽发动了进攻。在绝望与混乱之中,伊欧夫的第一美女蕾莉亚被梅萨蒂带走,而玛奇亚暗恋的少年克里姆也失踪了。玛奇亚虽然总算逃脱了,却失去了伙伴和归去之地……。' }, { img : 'https://lain.bgm.tv/pic/cover/l/0e/1e/218971_2y351.jpg', title: '朝花夕誓——于离别之朝束起约定之花', status: '已追完', progress: 100, jp: 'さよならの朝に約束の花をかざろう', time: '2018-02-24 SUN.', desc: ' 住在远离尘嚣的土地,一边将每天的事情编织成名为希比欧的布,一边静静生活的伊欧夫人民。在15岁左右外表就停止成长,拥有数百年寿命的他们,被称为“离别的一族”,并被视为活着的传说。没有双亲的伊欧夫少女玛奇亚,过着被伙伴包围的平稳日子,却总感觉“孤身一人”。他们的这种日常,一瞬间就崩溃消失。追求伊欧夫的长寿之血,梅萨蒂军乘坐着名为雷纳特的古代兽发动了进攻。在绝望与混乱之中,伊欧夫的第一美女蕾莉亚被梅萨蒂带走,而玛奇亚暗恋的少年克里姆也失踪了。玛奇亚虽然总算逃脱了,却失去了伙伴和归去之地……。' } ] .should-ellipsis{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:95%;}.should-ellipsis-full{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:100%;}.should-ellipsis i{position:absolute;right:24px;}.grey-text{color:#9e9e9e !important}.grey-text.text-darken-4{color:#212121 !important}html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}img{border-style:none}progress{display:inline-block;vertical-align:baseline}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}html{-webkit-box-sizing:border-box;box-sizing:border-box}*,*:before,*:after{-webkit-box-sizing:inherit;box-sizing:inherit}ul:not(.browser-default){padding-left:0;list-style-type:none}ul:not(.browser-default)>li{list-style-type:none}.card{-webkit-box-shadow:0 2px 2px 0 rgba(0,0,0,0.14),0 3px 1px -2px rgba(0,0,0,0.12),0 1px 5px 0 rgba(0,0,0,0.2);box-shadow:0 2px 2px 0 rgba(0,0,0,0.14),0 3px 1px -2px rgba(0,0,0,0.12),0 1px 5px 0 rgba(0,0,0,0.2)}.hoverable{-webkit-transition:-webkit-box-shadow .25s;transition:-webkit-box-shadow .25s;transition:box-shadow .25s;transition:box-shadow .25s,-webkit-box-shadow .25s}.hoverable:hover{-webkit-box-shadow:0 8px 17px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19);box-shadow:0 8px 17px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19)}i{line-height:inherit}i.right{float:right;margin-left:15px}.bangumi .right{float:right !important}.material-icons{text-rendering:optimizeLegibility;-webkit-font-feature-settings:'liga';-moz-font-feature-settings:'liga';font-feature-settings:'liga'}.row{margin-left:auto;margin-right:auto;margin-bottom:20px}.row:after{content:\"\";display:table;clear:both}.row .col{float:left;-webkit-box-sizing:border-box;box-sizing:border-box;padding:0 .75rem;min-height:1px}.row .col.s12{width:100%;margin-left:auto;left:auto;right:auto}@media only screen and (min-width:601px){.row .col.m6{width:50%;margin-left:auto;left:auto;right:auto}}html{line-height:1.5;font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,Oxygen-Sans,Ubuntu,Cantarell,\"Helvetica Neue\",sans-serif;font-weight:normal;color:rgba(0,0,0,0.87)}@media only screen and (min-width:0){html{font-size:14px}}@media only screen and (min-width:992px){html{font-size:14.5px}}@media only screen and (min-width:1200px){html{font-size:15px}}.card{position:relative;margin:.5rem 0 1rem 0;background-color:#fff;-webkit-transition:-webkit-box-shadow .25s;transition:-webkit-box-shadow .25s;transition:box-shadow .25s;transition:box-shadow .25s,-webkit-box-shadow .25s;border-radius:2px}.card .card-title{font-size:24px;font-weight:300}.card .card-title.activator{cursor:pointer}.card .card-image{position:relative}.card .card-image img{display:block;border-radius:2px 2px 0 0;position:relative;left:0;right:0;top:0;bottom:0;width:100%}.card .card-content{padding:24px;border-radius:0 0 2px 2px}.card .card-content p{margin:0}.card .card-content .card-title{display:block;line-height:32px;margin-bottom:8px}.card .card-content .card-title i{line-height:32px}.card .card-reveal{padding:24px;position:absolute;background-color:#fff;width:100%;overflow-y:auto;left:0;top:100%;height:100%;z-index:3;display:none}.card .card-reveal .card-title{cursor:pointer;display:block}.waves-effect{position:relative;cursor:pointer;display:inline-block;overflow:hidden;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;vertical-align:middle;z-index:1;-webkit-transition:.3s ease-out;transition:.3s ease-out}.waves-effect img{position:relative;z-index:-1}.waves-block{display:block}::-webkit-input-placeholder{color:#d1d1d1}::-moz-placeholder{color:#d1d1d1}:-ms-input-placeholder{color:#d1d1d1}::-ms-input-placeholder{color:#d1d1d1}[type=\"radio\"]:not(:checked){position:absolute;opacity:0;pointer-events:none}[type=\"radio\"]:not(:checked)+span{position:relative;padding-left:35px;cursor:pointer;display:inline-block;height:25px;line-height:25px;font-size:1rem;-webkit-transition:.28s ease;transition:.28s ease;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}[type=\"radio\"]:not(:checked)+span:before,[type=\"radio\"]:not(:checked)+span:after{border-radius:50%}[type=\"radio\"]:not(:checked)+span:before,[type=\"radio\"]:not(:checked)+span:after{border:2px solid #5a5a5a}[type=\"radio\"]:not(:checked)+span:after{-webkit-transform:scale(0);transform:scale(0)}[type=\"checkbox\"]:not(:checked){position:absolute;opacity:0;pointer-events:none}[type=\"checkbox\"]:not(:checked):disabled+span:not(.lever):before{border:none;background-color:rgba(0,0,0,0.42)}[type=\"checkbox\"].filled-in:not(:checked)+span:not(.lever):before{width:0;height:0;border:3px solid transparent;left:6px;top:10px;-webkit-transform:rotateZ(37deg);transform:rotateZ(37deg);-webkit-transform-origin:100% 100%;transform-origin:100% 100%}[type=\"checkbox\"].filled-in:not(:checked)+span:not(.lever):after{height:20px;width:20px;background-color:transparent;border:2px solid #5a5a5a;top:0px;z-index:0}input[type=checkbox]:not(:disabled) ~ .lever:active:before,input[type=checkbox]:not(:disabled).tabbed:focus ~ .lever::before{-webkit-transform:scale(2.4);transform:scale(2.4);background-color:rgba(0,0,0,0.08)}input[type=range].focused:focus:not(.active)::-webkit-slider-thumb{-webkit-box-shadow:0 0 0 10px rgba(38,166,154,0.26);box-shadow:0 0 0 10px rgba(38,166,154,0.26)}input[type=range].focused:focus:not(.active)::-moz-range-thumb{box-shadow:0 0 0 10px rgba(38,166,154,0.26)}input[type=range].focused:focus:not(.active)::-ms-thumb{box-shadow:0 0 0 10px rgba(38,166,154,0.26)} 番组计划 这里将是永远的回忆 window.onload = function(){ videos.forEach(function(video, i){ $('#rootRow').append(` ${video.title} ${video.jp} ${video.status} ${video.title} ${video.jp} 放送时间: ${video.time} ${video.desc} ${video.status} `) }) }","keywords":"B站"}],"posts":[{"title":"MIT-ALgo-lecture","slug":"MIT-ALgo-lecture","date":"2020-02-09T22:29:30.000Z","updated":"2020-02-16T10:09:09.458Z","comments":true,"path":"2020/02/10/MIT-ALgo-lecture/","link":"","permalink":"http://WigginsLi.github.io/2020/02/10/MIT-ALgo-lecture/","excerpt":"","text":"分享1-3集 MIT的算法公开课程( 原网址这里 ),包括英文、简体中文字幕 ps:如果有条件FQ的同学,在看视频的时候觉得英文字幕难懂的话,可以右键点击视频->复制视频链接->在浏览器中粘贴打开该链接,跳转到youtube上,右下角选择简体中文即可。 upd 2020.02.16 链接更新至16集, 并延期了分享时间至 2020.02.23 简体中文为谷歌机翻,可读性有待商榷,请搭配英文一同食用 ,视频软件推荐用 PotPlay 可实现双字幕 如果有同学很想继续看下去的话,可以在评论里提一下,我把后面的一起分享出来(人懒) 以下为下载地址(同一个压缩包里的是英文字幕) (1-16集) https://wss3.cn/f/1trv521icuj 中文字幕 https://wss3.cn/f/1trvlanqt9k 因为特意找了一个不限速的网盘,缺点是一次只能分享7天,所以如果链接过期了请留言或者直接私聊Orz/","categories":[{"name":"技术","slug":"技术","permalink":"http://WigginsLi.github.io/categories/技术/"}],"tags":[],"keywords":[{"name":"技术","slug":"技术","permalink":"http://WigginsLi.github.io/categories/技术/"}]},{"title":"2020_01_12","slug":"2020-01-12","date":"2020-01-12T22:35:32.000Z","updated":"2020-01-12T15:02:05.461Z","comments":true,"path":"2020/01/13/2020-01-12/","link":"","permalink":"http://WigginsLi.github.io/2020/01/13/2020-01-12/","excerpt":"","text":"套题 牛客练习赛57 A - 简单模拟 B - 算出每一轮的变化量直接除即可,注意最后一轮的细节(除出来的轮数-1,然后直接模拟最后一轮) C - 状压DP, 以dp[S]表示装好S集合需要的最少的箱子数 如果直接枚举集合,再枚举子集,从子集转移过来,需要 $O(3^n)$ . 那么我们反过来,枚举子集,再扩充成新集合,此时我们知道,在S中加入一个新的货物,可能原方案剩余的空间足够装这个货物, 那么$dp[S + 1<<i ] = dp[S]$.如果不够空间,就需要一个新的箱子,也就是dp[S]+1. 那么我们怎么知道是否有空间呢?我们可以同时维护一个L[S],表示 S 最后一个箱子的最大的剩余空间. 代码:牛客 D - 将Manacher算出来的数组整理成左右端点的形式,再注意一下细节.(有点难说Orz) 但其实回文树直接就可以做了 E - 代补 F - 不懂FFT,留给队友了 学习 了解 树链剖分及其简单应用 && 板子 主要思想是,将树结构转化为序列结构,利用剖分后子树序列连续的特性使用数据结构维护 了解 回文树 && 板子 近期计划 了解学习字符串相关 [] 了解学习图论相关","categories":[{"name":"每日小记","slug":"每日小记","permalink":"http://WigginsLi.github.io/categories/每日小记/"}],"tags":[],"keywords":[{"name":"每日小记","slug":"每日小记","permalink":"http://WigginsLi.github.io/categories/每日小记/"}]},{"title":"博弈论总结","slug":"game-theory","date":"2019-10-16T21:18:26.000Z","updated":"2019-10-17T13:25:19.744Z","comments":true,"path":"2019/10/17/game-theory/","link":"","permalink":"http://WigginsLi.github.io/2019/10/17/game-theory/","excerpt":"","text":"巴什博弈 能取$ [1-k] -> n\\%(k+1) $ 给定取数范围集合S -> SG函数打表 威佐夫博弈 $ a_k = \\lfloor \\frac{\\sqrt{5} + 1}{2} \\times k \\rfloor $ $ b_k = a_k + k $ Nim博弈及其变种 N:必胜态 P:必败态 原型 $ a_1 \\bigoplus a_2 \\bigoplus \\cdot \\cdot \\cdot \\bigoplus a_n = X \\not= 0 -> N$ 策略:每次取使得异或和为0即可, $ 取a_i堆当且仅当 a_i\\bigoplus X < a_i $ N的后继状态必有P NimK Nim拓展到每次可以取k堆 每个数转为二进制 -> 每个二进制位求和并%(k+1) -> 所有位为0则P 阶梯Nim 只取 奇数位 进行Nim和即可 题目中一般, 取数会有限制, 剩余个数需要和相邻的堆数有关,这时候可以把相邻的差值转化为阶梯Nim SG $ SG[i] = mex{SG[k_i]} , i的后继状态k_i $ 如果只需要知道是否先手必胜,可以把SG函数简化为0/1状态 如果需要进行Nim和操作,必须求出SG函数 Multi-SG 将一个游戏分成多个游戏,并分别求SG值进行Nim和即可 Every-SG 先手必胜当且仅当单一游戏中最大的step为奇数 $$ step(v) = \\begin{cases} 0, & \\text{v为终止状态} \\ max(step(u))+1, & \\text{SG(v)>0 && u为v的后继状态 && SG(u) = 0} \\ min(step(u))+1, & \\text{SG(v)=0 && u为v的后继状态} \\end{cases}$$ 斐波那契博弈 每次取不能超过对手上次取的两倍 必败态 当且仅当 堆数为斐波那契数列的项 Anti-SG 先拿完的输 SJ定理 对于任意一个 Anti-SG 游戏, 如果我们规定当局面中所有的单一游戏的SG值为0时,游戏结束,则先手必胜当且仅当: 游戏的SG函数不为0且游戏中某个单一游戏的SG函数大于1 游戏的SG函数为0且游戏中没有单一游戏的SG大于1 无向图的删边游戏 叶子节点的SG值为0; 中间节点的SG值为它的所有子节点的SG值加1后的异或和. 存在环的时候 将图中的任意一个偶环缩成一个新点,任意一个奇环缩成一个新点加一个新边:所有连到原本环上的边全部都改成与新点相连.这样的改动不会影响一个图的SG值 对偶博弈 完全对称的图,所以先手或者后手必胜","categories":[{"name":"技术","slug":"技术","permalink":"http://WigginsLi.github.io/categories/技术/"}],"tags":[],"keywords":[{"name":"技术","slug":"技术","permalink":"http://WigginsLi.github.io/categories/技术/"}]},{"title":"技术部二面试题","slug":"jsbem","date":"2019-09-09T18:31:59.000Z","updated":"2019-09-09T10:34:35.364Z","comments":true,"path":"2019/09/10/jsbem/","link":"","permalink":"http://WigginsLi.github.io/2019/09/10/jsbem/","excerpt":"","text":"流程: 思考5min时间,在下面7个问题中选择一个问题 思考20min 轮流上台向大家讲解自己选择问题的解法 蚂蚁 n只蚂蚁以每秒1cm的速度在长为$L$cm的杆子上爬行.当蚂蚁爬到杆子的端点时就会掉落.由于杆子太细,两只蚂蚁相遇时,它们不能交错通过,只能各自爬回去.对于每只蚂蚁,我们知道它距离杆子左端的距离$x_i$,但不知道它当前的朝向.请计算所有蚂蚁落下杆子所需的最短时间和最长时间. 货仓选址 在一条数轴上有N家商店,它们的坐标分别为A[1]~A[N].现在需要在数轴上建立一家货仓,每天清晨,从货仓到每家商店都要运送一车货品.为了提高效率,求把货仓建在何处,可以使得货仓到每家商店的距离之和最小 简单数学 设x, y满足约束条件$\\begin{cases} 3x-y-6 \\leq 0, \\ x-y+2 \\geq 0, \\ x \\geq 0, y \\geq 0 \\end{cases}$,若目标函数z=ax+by(a>0, b>0) 的值是最大值为12,则 $\\frac{2}{a} + \\frac{3}{b}$ 的最小值为 ____ 等比数列 已知等比数列{ $a_n$ }的前n项和为$S_n=a*2^n+b$,且$a_1$ = 3. 求a,b 的值及数列{ $a_n$ }的通项公式 简单物理 在两块相同的竖直木板之间,有质量均为$m$的$n$块相同的块,用两个大小均为$F$的水平力压木板,使砖静止不动,则第 $i$ 块对第 $i+1$ 块摩擦力大小为____ 赛车 10部长度为2的赛车分别位于x轴的正半轴上的(2,5,9,14,20,25,29,34,40,50)(车头位置). 每辆车一起向负半轴前进,速度分别为(1,2,3,4,5,6,7,8,9,10) ,后面的车不能超越前面的车(这意味着他们可能会减速),问第10辆车车尾通过零点的时间. 发挥你的特长 如果上面的题目都不合你的口味,没关系,选择一个你熟悉并且感兴趣的领域的术语,尽量浅显易懂地阐述出来^_^. (eg:勾股定理、乐理、板绘)","categories":[{"name":"技术","slug":"技术","permalink":"http://WigginsLi.github.io/categories/技术/"}],"tags":[],"keywords":[{"name":"技术","slug":"技术","permalink":"http://WigginsLi.github.io/categories/技术/"}]},{"title":"__int128类型小记","slug":"int128","date":"2019-07-19T06:31:10.467Z","updated":"2019-08-09T11:06:59.587Z","comments":true,"path":"2019/07/19/int128/","link":"","permalink":"http://WigginsLi.github.io/2019/07/19/int128/","excerpt":"范围是 $[- 2^{127} , 2^{127}-1]$ 基本用法与基本数据类型类似,但不能用$cin,cout$读入输出 这里记下==输入输出“外挂”==:","text":"范围是 $[- 2^{127} , 2^{127}-1]$ 基本用法与基本数据类型类似,但不能用$cin,cout$读入输出 这里记下==输入输出“外挂”==: 读入template <typename T> inline void read(T &X) { X = 0; char ch = 0; T op = 1; for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') op = -1; for(; ch >= '0' && ch <= '9'; ch = getchar()) X = (X << 3) + (X << 1) + ch - 48; X *= op; } 输出void _print(__int128 x) { if(x>9) _print(x/10); putchar(x%10+'0'); } void print(__int128 x) { if(x<0) {x=-x;putchar('-');} _print(x); }","categories":[{"name":"Experience","slug":"Experience","permalink":"http://WigginsLi.github.io/categories/Experience/"}],"tags":[{"name":"数据类型","slug":"数据类型","permalink":"http://WigginsLi.github.io/tags/数据类型/"}],"keywords":[{"name":"Experience","slug":"Experience","permalink":"http://WigginsLi.github.io/categories/Experience/"}]},{"title":"如何更稳定地科学上网-记vultr被墙后的v2ray+WebSocket+TLS部署过程","slug":"v2ray-deploy","date":"2019-06-16T14:02:20.000Z","updated":"2019-08-09T11:07:06.917Z","comments":true,"path":"2019/06/16/v2ray-deploy/","link":"","permalink":"http://WigginsLi.github.io/2019/06/16/v2ray-deploy/","excerpt":"$Github Page$ 姑且算是国外的网站吧,在上面记一下科学上网应该没问题吧(狗头保命 <本文仅为技术交流,请勿转载>","text":"$Github Page$ 姑且算是国外的网站吧,在上面记一下科学上网应该没问题吧(狗头保命 <本文仅为技术交流,请勿转载> 前言:经过六月的大事件,国内大多数VPN被封禁,各大知名服务器厂商的IP也被GFW墙了。然后很不幸地,我们买的vultr的服务器就是受害者之一。本着不能浪费账户里剩下的3刀的精神(一台服务器一个月才5刀呐),遂上网求解,终于找到Websocket+TLS的解决方案(其实谷歌一下一大堆,但是都有些问题,这里做一下整合)。 需要准备的: 一个暂时能用的美国代理(ss,ssr,sockets5,etc)(网上免费代理一大堆) 一个域名(免费的就行,下文会讲) 一台服务器 $Xshell$(可以申请教育版,这里不赘述) v2ray客户端(https://github.com/2dust/v2rayN/releases),下载最大的那个解压就好了 服务器选择服务器可以选择$vultr$,或者搬瓦工,这里笔者用的$vultr$。可以点这个链接注册,if so,你将可以得到50刀(一个月过期) 配置正常地点网页右上角的加号Deploy New Server新开一个服务器即可,建议选择美国的服务器,笔者选择Los Angeles,操作系统选Debian 9 64位(注意这篇文章的时效性),其他默认即可。 检测可以点这里,输入你的服务器的域名,点击ping检测也可以用cmd ping一下,被墙的ip就会显示连接超时,(也就有了这篇文章┭┮﹏┭┮ 免费域名Freenom(此步骤需挂代理) 点击这里 进入网站, 点右上角的登入进入登陆界面,等待网页加载完全,用下方的谷歌账号登陆。(如果提示登陆失败,没关系,回到主页,不要一直尝试登陆,网站会识别你是机器人,很麻烦)。 在主页的搜索框输入你想要的域名,查找,(如果没有显示免费,就换一个,一般长一点的才免费)选好之后,点完成,进入购物车 (这时候一般都是英文界面),选择12个月的免费,点continue,点右下角继续用谷歌登陆,这时候网站就会给你的Gmail发激活账户的邮件,点进去激活链接激活一下,然后再登陆就可以了。(如果不成功,多试几次,频率不要太高,或者也可以试下左下角的邮箱注册,emmm注册这一步卡了我蛮久的) 在主页查看Services-My Domains,确保域名处于ACTIVE状态。 设置中间服务商 简单介绍一下为什么有这一步。前面提到我们的ip被墙了,那么一个破解的思路是我们连接上一个没有被墙的服务器,再通过这个服务器连接我们被墙的服务器,相当于桥的作用。 CloudFlare 注册,比较简单 登陆之后,提示Add your site,把刚刚注册的域名填进去,点下一步,选择免费套餐 提示要更换Name Servers,回到freenom的My Domain,点击域名后面的Manage Domain。 进入cloudflare主页上方的DNS选项卡,添加一个A记录,name填www,ip填你的vultr服务器的地址,(重要!!!),后面的云朵点一下点成灰色,点击添加 之后隔一段时间去网站ping一下你的域名,如果ip归属地大多数都显示美国 cloudflare,那么就可以进行下一步。 对服务器操作Xshell连接服务器 首先在cmd ping一下域名看解析的ip是不是cloudflare的,确定你所在的地区已经被解析 这一步参考博文.ps:如果连不上/显示错误的话,检查信息是否填写正确,笔者就把服务器地址填成了域名,郁闷了半天Orz 配置V2ray环境 这里我们使用233大神的一键安装脚本 输入bash <(curl -s -L https://git.io/v2ray.sh) 如果提示 curl: command not found ,那是因为你的 VPS 没装 Curl ubuntu/debian 系统安装 Curl 方法: apt-get update -y && apt-get install curl -y centos 系统安装 Curl 方法: yum update -y && yum install curl -y 安装好 curl 之后就能安装脚本了 ps:如果这个脚本链接挂了,可以用我 Fork 的一份副本bash <(curl -L -s https://git.io/fj2lI) 大概是下面这个界面,我们选择 Websocket+TLS 协议 V2Ray 端口随便,不要是 80 和 443 即可,然后输入你的域名,域名解析 Y ,自动配置 TLS 也是 Y ,其他就默认吧,一路回车。等待安装完成 调整中间服务商 回到 CloudFlare 主页,选择 Crypto 选项卡的 SSL 为 Full。确保左边显示 Universal SSL Status Active Certificate。如果没有,24小时内就会显示,可以先不管。继续下一步。 点击 DNS 选项卡,把刚刚那个点灰的云朵再点一下,点成橙色。 至此完成全部配置。 尝试连接v2ray Xshell连接服务器,输入v2ray qr,把得到的链接复制出来,在浏览器打开 得到一张二维码,打开V2ray客户端,即解压后文件夹里的 v2rayN.exe 右击托盘的程序,选择扫描屏幕二维码,然后把客户端完全关掉,重新打开 右击托盘程序,把 启用http代理 的勾勾上 在客户端里面选择参数设置->路由设置->路由模式->绕过局域网及大陆地址,点确定 再右击v2ray节点,测试一下服务器延迟,如果不是显示 -1ms ,就说明成功连接。 至此大功告成。 如果出现什么问题,可以留言问我,看到就会回复。。。","categories":[{"name":"科学上网","slug":"科学上网","permalink":"http://WigginsLi.github.io/categories/科学上网/"}],"tags":[{"name":"v2ray","slug":"v2ray","permalink":"http://WigginsLi.github.io/tags/v2ray/"}],"keywords":[{"name":"科学上网","slug":"科学上网","permalink":"http://WigginsLi.github.io/categories/科学上网/"}]},{"title":"p3080 [USACO13MAR]牛跑The Cow Run [区间DP]","slug":"p3080","date":"2019-05-29T21:07:47.000Z","updated":"2019-05-29T14:17:34.535Z","comments":true,"path":"2019/05/30/p3080/","link":"","permalink":"http://WigginsLi.github.io/2019/05/30/p3080/","excerpt":"$\\implies$ 传送门 题目大意: 给定一条坐标轴上$n$个点相对于原点的位置(正负均有) 从原点出发去占领这些点,被占领前每个点单位时间消耗一个单位代价 每单位时间可以移动一个单位长度,问最少花费多少代价","text":"$\\implies$ 传送门 题目大意: 给定一条坐标轴上$n$个点相对于原点的位置(正负均有) 从原点出发去占领这些点,被占领前每个点单位时间消耗一个单位代价 每单位时间可以移动一个单位长度,问最少花费多少代价 分析: 容易想出这题应该用区间DP求解,但是本题多了一个状态,即每次处理完一段区间,可能处于左端点,也可能是右端点 那么考虑三维状态 $dp[l][r][0/1]$ 代表处理 $[l,r]$ 区间后处于左/右端点的最小代价 将n个坐标加上原点c离散化成$n+1$个点,那么我们初始化$dp[c][c][0]=dp[c][c][1]=0$ 当位于 $[l,r]$ 区间的左端点时, 由 $[l+1,r]$ 的左端点到达,代价 $(p[l+1]-p[l]) * (n+1-(r-l))$ //除了[l+1,r]的部分(包括l点)都需要消耗代价. 由 $[l+1,r]$ 的右端点到达,代价 $(p[r]-p[l]) * (n+1-(r-l))$ 当位于 $[l,r]$ 区间的右端点亦同理可得. 得到转移方程:$$ dp[l][r][0] = min( \\begin{cases} dp[l+1][r][0]+(p[l+1]-p[l]) * (n+1-(r-l)) \\\\ dp[l+1][r][1]+(p[r]-p[l])_ (n+1-(r-l)) \\end{cases}) $$ $$ dp[l][r][1] = min( \\begin{cases} dp[l][r-1][1]+(p[r]-p[r-1]) * (n+1-(r-l)) \\\\ dp[l][r-1][0]+(p[r]-p[l])_ (n+1-(r-l)) \\end{cases}) $$ Code #include <bits/stdc++.h> using namespace std; typedef long long ll; const int INF = 0x3f3f3f3f; const int maxn = 1e3+5; ll p[maxn],dp[maxn][maxn][2]; int n; int main() { cin>>n; for(int i=1;i<=n;i++) cin>>p[i]; p[n+1]=0; sort(p+1,p+n+2); int c=lower_bound(p+1,p+n+2,0)-p; memset(dp,INF,sizeof(dp)); //cout<<c<<endl; dp[c][c][0]=dp[c][c][1]=0; for(int len=2;len<=n+1;len++) { for(int i=1;i+len-1<=n+1;i++) { int j=i+len-1; dp[i][j][0]=min(dp[i+1][j][0]+(p[i+1]-p[i])*(n+1-(j-i)),dp[i+1][j][1]+(p[j]-p[i])*(n+1-(j-i))); dp[i][j][1]=min(dp[i][j-1][1]+(p[j]-p[j-1])*(n+1-(j-i)),dp[i][j-1][0]+(p[j]-p[i])*(n+1-(j-i))); //cout<<i<<" "<<j<<" "; //cout<<dp[i][j][0]<<" "<<dp[i][j][1]<<endl; } } cout<<min(dp[1][n+1][0],dp[1][n+1][1])<<endl; return 0; }","categories":[{"name":"动态规划","slug":"动态规划","permalink":"http://WigginsLi.github.io/categories/动态规划/"}],"tags":[{"name":"dp","slug":"dp","permalink":"http://WigginsLi.github.io/tags/dp/"}],"keywords":[{"name":"动态规划","slug":"动态规划","permalink":"http://WigginsLi.github.io/categories/动态规划/"}]},{"title":"cf-1114D Flood Fill [区间DP]","slug":"cf-1114D","date":"2019-05-29T18:58:26.000Z","updated":"2019-05-29T14:17:11.031Z","comments":true,"path":"2019/05/30/cf-1114D/","link":"","permalink":"http://WigginsLi.github.io/2019/05/30/cf-1114D/","excerpt":"$\\implies$ 传送门 题目大意: 给定$n$个方块有相应的颜色$c_i$,定义相邻的方块颜色相同则可以称为一个联通颜色块. 现在让你初始选定一个方块$p$,每次可以将该方块所在的联通颜色块换成一个颜色,求最少的操作次数使得$n$个方块全部为一种颜色","text":"$\\implies$ 传送门 题目大意: 给定$n$个方块有相应的颜色$c_i$,定义相邻的方块颜色相同则可以称为一个联通颜色块. 现在让你初始选定一个方块$p$,每次可以将该方块所在的联通颜色块换成一个颜色,求最少的操作次数使得$n$个方块全部为一种颜色 分析: 定义 $dp[l][r]$ 表示$l,r$区间全部变为同一种颜色的代价,容易得出$dp$转移方程: $dp[l][r] = min(dp[l][k]+dp[k+1][r]+1,dp[l][r])$ 上述转移方程复杂度为$O(n^3)$,需要优化 改写方程形式为$$dp[l][r]=\\begin{cases} dp[l+1][r-1], & \\text {if a[l]=a[r]} \\\\ min(dp[l+1][r]+1,dp[l][r-1]+1), & \\text {if a[l] $\\neq$ a[r]} \\end{cases}$$ 一般套路,理解记忆 Code: #include <bits/stdc++.h> using namespace std; int dp[5005][5005],n,a[5005],tot,b[5005]; int main() { cin>>n; for(int i=1;i<=n;i++) { scanf("%d",&a[i]); if(a[i]==a[i-1]) { continue; } else { b[++tot]=a[i]; } } n=tot; for(int len=1;len<n;len++) { for(int l=1;l+len<=n;l++) { int r=l+len; if(b[l]==b[r]) { dp[l][r]=dp[l+1][r-1]+1; } else { dp[l][r]=min(dp[l+1][r]+1,dp[l][r-1]+1); } } } cout<<dp[1][n]; }","categories":[{"name":"动态规划","slug":"动态规划","permalink":"http://WigginsLi.github.io/categories/动态规划/"}],"tags":[{"name":"区间dp","slug":"区间dp","permalink":"http://WigginsLi.github.io/tags/区间dp/"},{"name":"dp","slug":"dp","permalink":"http://WigginsLi.github.io/tags/dp/"}],"keywords":[{"name":"动态规划","slug":"动态规划","permalink":"http://WigginsLi.github.io/categories/动态规划/"}]},{"title":"洛谷p1119-灾后重建 [floyd]","slug":"p1119","date":"2019-05-11T11:15:57.000Z","updated":"2019-05-28T14:38:40.710Z","comments":true,"path":"2019/05/11/p1119/","link":"","permalink":"http://WigginsLi.github.io/2019/05/11/p1119/","excerpt":"$\\implies$ 传送门 题目大意: 给出图中$x_i$节点被加进图里的时间$t_i$;多个询问$Q$,求$t_j$时间,$x_j,y_j$的最短距离","text":"$\\implies$ 传送门 题目大意: 给出图中$x_i$节点被加进图里的时间$t_i$;多个询问$Q$,求$t_j$时间,$x_j,y_j$的最短距离 分析: 看这题之前我们先回顾一下$floyd$算法; 第一层循环实际上是将$1-n$节点依次加入到图中;每加入一个,通过内层循环更新图中已有的点之间的距离 那么回到这题,在时间上依次将节点加入即天然为$floyd$中的第一层序 那么我们可以记录每个加入时间的状态,查询时二分查找$t_j$时间介于哪两个状态之间,直接输出$x_j,y_j$当前状态的最短距离即可 代码: #include <bits/stdc++.h> using namespace std; const int maxn = 1e5+5; const int INF=0x3f3f3f3f; int n,m,u,v,w,q,t[205]; int dp[205][205][205]; int main() { cin>>n>>m; for(int i=0;i<n;i++) cin>>t[i]; memset(dp,INF,sizeof(dp)); for(int i=1;i<=m;i++) { cin>>u>>v>>w; dp[0][u][v]=dp[0][v][u]=w; } for(int i=0;i<n;i++) dp[0][i][i]=0; for(int k=0;k<n;k++) { for(int i=0;i<n;i++) { for(int j=0;j<n;j++) { dp[k][i][j]=min(dp[k==0?0:k-1][i][k]+dp[k==0?0:k-1][k][j],dp[k==0?0:k-1][i][j]); } } } cin>>q; while(q--) { cin>>u>>v>>w; int inx=upper_bound(t,t+n,w)-t-1; //cout<<inx<<" "<<t[u]<<" "<<t[v]<<endl; if(t[u]>w||t[v]>w||dp[inx][u][v]==INF) { cout<<-1<<endl; }else { cout<<dp[inx][u][v]<<endl; } } return 0; }","categories":[{"name":"动态规划","slug":"动态规划","permalink":"http://WigginsLi.github.io/categories/动态规划/"},{"name":"思维","slug":"思维","permalink":"http://WigginsLi.github.io/categories/思维/"}],"tags":[{"name":"floyd","slug":"floyd","permalink":"http://WigginsLi.github.io/tags/floyd/"}],"keywords":[{"name":"动态规划","slug":"动态规划","permalink":"http://WigginsLi.github.io/categories/动态规划/"},{"name":"思维","slug":"思维","permalink":"http://WigginsLi.github.io/categories/思维/"}]},{"title":"ACM协会+青创课第二节KMP讲解资料","slug":"kmp-teaching","date":"2019-05-06T01:00:51.000Z","updated":"2019-05-27T12:07:16.072Z","comments":true,"path":"2019/05/06/kmp-teaching/","link":"","permalink":"http://WigginsLi.github.io/2019/05/06/kmp-teaching/","excerpt":"一篇讲的很好的博客, $ \\implies $ 这里 PPT下载 $\\implies$ 百度网盘 提取码: pqs9或者复制这一段: 链接: https://pan.baidu.com/s/13O9WiAkaQYDrKE1UMJAS4w 提取码: pqs9 复制这段内容后打开百度网盘手机App,操作更方便哦","text":"一篇讲的很好的博客, $ \\implies $ 这里 PPT下载 $\\implies$ 百度网盘 提取码: pqs9或者复制这一段: 链接: https://pan.baidu.com/s/13O9WiAkaQYDrKE1UMJAS4w 提取码: pqs9 复制这段内容后打开百度网盘手机App,操作更方便哦 最后的最后,非常感谢大家能抽空出来听我的课,(鞠躬敬礼)讲得不好的地方请多多担待有问题或者想和我交流,请随时留言","categories":[{"name":"字符串","slug":"字符串","permalink":"http://WigginsLi.github.io/categories/字符串/"}],"tags":[{"name":"KMP","slug":"KMP","permalink":"http://WigginsLi.github.io/tags/KMP/"}],"keywords":[{"name":"字符串","slug":"字符串","permalink":"http://WigginsLi.github.io/categories/字符串/"}]},{"title":"【04.11训练赛记录】","slug":"19-4-11-Training","date":"2019-04-12T19:57:36.000Z","updated":"2019-08-09T11:06:43.570Z","comments":true,"path":"2019/04/13/19-4-11-Training/","link":"","permalink":"http://WigginsLi.github.io/2019/04/13/19-4-11-Training/","excerpt":"出题数垫底祭QAQ $\\implies$题目文档PDF(似乎只有同一个group才能打开) 打不开的小伙伴请自行搜索(20172018-acmicpc-pacific-northwest-regional-contest-div-1)(づ ̄3 ̄)づ","text":"出题数垫底祭QAQ $\\implies$题目文档PDF(似乎只有同一个group才能打开) 打不开的小伙伴请自行搜索(20172018-acmicpc-pacific-northwest-regional-contest-div-1)(づ ̄3 ̄)づ A. Odd Palindrome 字符串水题,还没看题面已经被$Inctry$切了 C. Fear Factoring 大意:求$\\sum_{i=a}^b F(i)$,其中$F(N)=\\sum_{x \\mid N}$,数据范围$(1 \\leqslant a \\leqslant b \\leqslant 10^{12}) 且 (1 \\leqslant b - a \\leqslant 10^{6})$ 如果直接暴力,1e6*sqrt(1e12)的复杂度完全承受不了 反过来想,如果对于每个数$x \\in (0,10^6)$,如果它是区间内某些数的因数,计算它对区间内和的贡献即可 View Codec++ #include <bits/stdc++.h> using namespace std; #define PB push_back typedef long long ll; typedef pair<int,int> pii; const int maxn = 1e6+5; const int INF = 0x3f3f3f3f; ll l,r; int main() { cin>>l>>r; ll m=sqrt(r+0.5); ll ans=0; for(ll i=1;i<=m;i++) { ll st=(l+(i-1))/i;//向上取整 //cout<<"i:"<<i<<"st:"<<st<<endl; for(ll j=st;j*i<=r;j++) { if(j>m) ans+=i+j;//cout<<"* "<<i<<" "<<j<<" "; else ans+=i;//cout<<"** "<<i<<" "; } //cout<<endl; } cout<<ans<<endl; return 0; }","categories":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"}],"tags":[],"keywords":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"}]},{"title":"CF-1141E-Superhero Battle【数学+细节】","slug":"cf-1141E","date":"2019-03-20T23:43:15.000Z","updated":"2019-03-20T15:55:18.113Z","comments":true,"path":"2019/03/21/cf-1141E/","link":"","permalink":"http://WigginsLi.github.io/2019/03/21/cf-1141E/","excerpt":"$\\implies$传送门 乍看下是水题,结果wa得措不及防","text":"$\\implies$传送门 乍看下是水题,结果wa得措不及防 大意:给出boss的血量H,并给出每一回合打出的n轮伤害(boss可以加血),问多少轮后boss被打败 分析: 容易想到,计算出每一回合打出的总伤害u,用H膜这个伤害不就好了,但这样仍有弊端 存在一些情况,比如在膜出来的上一个回合boss已经被打死了(因为boss在后半回合补血,看起来总伤害低了,但前面的伤害足以打死) 为了解决这个问题,我们取出一回合中能对boss造成的最高伤害m(负数) 那么就可以用(H+m)%u得到boss被打死前的回合,之后再循环几次回合即可 代码: #include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 2e5+5; const int INF=0x3f3f3f3f; ll H,n,d[maxn],u=0,ans=0,minn=INF; int main() { cin>>H>>n; for(int i=1;i<=n;i++) { cin>>d[i]; u+=d[i]; minn=min(minn,u); if(H>0) H+=d[i],ans++; } if(H>0) { if(u>=0) { puts("-1"); return 0; }else { ll t=(H+minn)/(-u); H+=u*t; ans+=n*t; while(H>0) { for(int i=1;i<=n;i++) { ans++; H+=d[i]; if(H<=0) { cout<<ans<<endl; return 0; } } } } }else { cout<<ans<<endl; } }","categories":[{"name":"数论","slug":"数论","permalink":"http://WigginsLi.github.io/categories/数论/"}],"tags":[{"name":"数学","slug":"数学","permalink":"http://WigginsLi.github.io/tags/数学/"}],"keywords":[{"name":"数论","slug":"数论","permalink":"http://WigginsLi.github.io/categories/数论/"}]},{"title":"CF-1141F2-Same Sum Blocks (Hard)【数据结构+贪心】","slug":"cf-1141F2","date":"2019-03-20T23:42:56.000Z","updated":"2019-03-20T16:15:35.746Z","comments":true,"path":"2019/03/21/cf-1141F2/","link":"","permalink":"http://WigginsLi.github.io/2019/03/21/cf-1141F2/","excerpt":"$\\implies$传送门 学习对$map$的灵活应用","text":"$\\implies$传送门 学习对$map$的灵活应用 大意:求最多的不相交区间,使它们的和相等$(n<=2000)$ 分析: 假设我们知道和为$a$时的全部序列(包括相交的、不相交的),那么如何选出当前的最多不相交序列? 我们贪心的策略是:选择最前面的序列,然后依次选择不与前一个序列相交的序列,即可(参考《紫书》$P_{232}$) 那么现在的问题是如何得到关于每个值$a$的全部序列 我们定义$map<a,vector<pair>>$,将$a$的全部序列存入$vector$中 那么只预处理出前缀和,然后$n^2$保存进$vector$即可 $ps$:附上神仙代码 代码: #include <bits/stdc++.h> using namespace std; #define VPII vector<pair<int,int> > const int maxn = 2e3; int n,x,s[maxn]; map<int,VPII> slr; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) {cin>>x;s[i]+=s[i-1]+x;} for(int r=1;r<=n;r++) { for(int l=1;l<=r;l++) { slr[s[r]-s[l-1]].push_back(make_pair(l,r)); } } VPII ans; for(auto &ele:slr) { VPII p=ele.second; int nowr=-1; VPII tmp; for(auto &u:p) { if(u.first>nowr) { nowr=u.second; tmp.push_back(u); } } if(tmp.size()>ans.size()) { ans=tmp; } } cout<<ans.size()<<endl; for(auto &e:ans) { cout<<e.first<<" "<<e.second<<endl; } return 0; } 神仙代码: #include<bits/stdc++.h> using namespace std; #define rep(i,a,b) for(int i=(a);i<=(b);++i) #define MP make_pair #define PB push_back #define VPII vector<pair<int,int>> int n,x,s[1510]; unordered_map<int, VPII> slr; int main() { scanf("%d",&n);s[0]=0; rep(i,1,n)scanf("%d",&x),s[i]=s[i-1]+x; rep(r,1,n)rep(l,1,r) slr[s[r]-s[l-1]].PB(MP(r,l)); VPII ans; for(auto &ele:slr){ VPII lr = ele.second; int nowr=-1; VPII tmp; for(auto x:lr)if(x.second>nowr){ tmp.PB(x);nowr=x.first; } if(tmp.size()>ans.size()) ans = tmp; } printf("%d\\n",ans.size()); for(auto x:ans) printf("%d %d\\n", x.second, x.first); } unordered_map比map更适合查找,时间复杂度为O(1)","categories":[{"name":"数据结构","slug":"数据结构","permalink":"http://WigginsLi.github.io/categories/数据结构/"},{"name":"贪心","slug":"贪心","permalink":"http://WigginsLi.github.io/categories/贪心/"}],"tags":[{"name":"map","slug":"map","permalink":"http://WigginsLi.github.io/tags/map/"}],"keywords":[{"name":"数据结构","slug":"数据结构","permalink":"http://WigginsLi.github.io/categories/数据结构/"},{"name":"贪心","slug":"贪心","permalink":"http://WigginsLi.github.io/categories/贪心/"}]},{"title":"CF-414B-Mashmokh and ACM【DP】","slug":"cf414B","date":"2019-03-20T22:39:44.000Z","updated":"2019-03-20T15:17:04.355Z","comments":true,"path":"2019/03/21/cf414B/","link":"","permalink":"http://WigginsLi.github.io/2019/03/21/cf414B/","excerpt":"$\\implies$ 传送门 简单dp,学一下套路","text":"$\\implies$ 传送门 简单dp,学一下套路 大意:给定$n,k$,求满足条件的长度为$k$的序列数量,条件是$[1,n]$个数字的组合,要求后一个数字可以整除前一个数字 分析: 定义$f[len][n]$表示长度为$len$的最后一位为$n$的序列数 对于最后一位之前的状态都是确定的 那么得到转移方程$f[len][n]+=f[len-1][k]\\,\\,\\,\\,(n\\%k==0)$ 如果暴力出k的话需要$n^3$,时间上承受不了,我的办法是先用$n^2$预处理出k存起来那么最后时间复杂度为$n^2*count(k)$ 看别人代码时发现可以在转移时由$len$转移到$len+1$,这样就不需要预处理,直接倍增做 代码: #include <bits/stdc++.h> using namespace std; const int mod=1e9+7; int f[2005][2005],n,k; vector<int> cut[2005]; int main() { cin>>n>>k; for(int i=1;i<=n;i++) f[1][i]=1; for(int i=1;i<=n;i++) { for(int j=1;j<=i;j++) { if(i%j==0) cut[i].push_back(j); } } for(int len=2;len<=k;len++) { for(int i=1;i<=n;i++) { for(int j=0;j<cut[i].size();j++) { f[len][i]+=f[len-1][cut[i][j]]; f[len][i]%=mod; } } } int ans=0; for(int i=1;i<=n;i++) { ans+=f[k][i]; ans%=mod; } cout<<ans<<endl; return 0; } 另一种写法: #include<bits/stdc++.h> using namespace std; #define ll long long #define mod 1000000007 ll n,k,f[3000][3000]; int main() { //freopen("input.in","r",stdin); cin>>n>>k; for(int j=1;j<=n;j++)f[1][j]=1; for(int i=1;i<k;i++) { for(int j=1;j<=n;j++) { for(int k=j;k<=n;k+=j)f[i+1][k]+=f[i][j],f[i+1][k]%=mod; } } ll ans=0; for(int i=1;i<=n;i++)ans=(ans+f[k][i])%mod; cout<<ans<<endl; return 0; }","categories":[{"name":"动态规划","slug":"动态规划","permalink":"http://WigginsLi.github.io/categories/动态规划/"}],"tags":[{"name":"dp","slug":"dp","permalink":"http://WigginsLi.github.io/tags/dp/"}],"keywords":[{"name":"动态规划","slug":"动态规划","permalink":"http://WigginsLi.github.io/categories/动态规划/"}]},{"title":"hdu-6470-Count【矩阵快速幂+二项式定理】","slug":"hdu-6470","date":"2019-03-18T23:26:52.000Z","updated":"2019-05-05T16:58:33.874Z","comments":true,"path":"2019/03/19/hdu-6470/","link":"","permalink":"http://WigginsLi.github.io/2019/03/19/hdu-6470/","excerpt":"$\\implies$传送门 加深了对矩阵快速幂的理解","text":"$\\implies$传送门 加深了对矩阵快速幂的理解 大意:$已知F(n)=2\\times F(n-2)+F(n-1)+n^3,求F(n)mod123456789$ 分析: 如果只是普通的递推式,那么直接套矩阵快速幂模板就可以了,但现在有$n^3$这一项 考虑到二项式定理:$n^3=(n-1+1)^3=(n-1)^3+3\\cdot(n-1)^2+3\\cdot(n-1)+1$ 那么可以构造矩阵: $$ \\begin{bmatrix} F(n-1) \\\\ F(n) \\\\ n^3 \\\\ n^2 \\\\ n \\\\ 1 \\\\ \\end{bmatrix} = \\begin{bmatrix} 0 & 1 & 0 & 0 & 0 & 0 \\\\ 2 & 1 & 1 & 3 & 3 & 1 \\\\ 0 & 0 & 1 & 3 & 3 & 1 \\\\ 0 & 0 & 0 & 1 & 2 & 1 \\\\ 0 & 0 & 0 & 0 & 1 & 1 \\\\ 0 & 0 & 0 & 0 & 0 & 1 \\\\ \\end{bmatrix} \\cdot \\begin{bmatrix} F(n-2) \\\\ F(n-1) \\\\ (n-1)^3 \\\\ (n-1)^2 \\\\ (n-1) \\\\ 1 \\\\ \\end{bmatrix}$$ 矩阵快速幂的关键就是构造n这一项和前面几项的关系,得到递推式,得到矩阵$\\implies$ AC ヾ(◍°∇°◍)ノ゙继续努力叭 代码: #include <bits/stdc++.h> using namespace std; typedef long long ll; const int mod=123456789; struct martix{ ll mo[7][7]; martix() { memset(mo,0,sizeof(mo)); } }; martix g,m; martix mul(martix a,martix b) { martix c; for(int i=0;i<6;i++) { for(int j=0;j<6;j++) { for(int k=0;k<6;k++) { c.mo[i][j]=(c.mo[i][j]+a.mo[i][k]*b.mo[k][j])%mod; } } } return c; } martix powmo(martix a,ll n) { martix T; for(int i=0;i<6;i++) { T.mo[i][i]=1; } while(n) { if(n&1) T=mul(T,a); n>>=1; a=mul(a,a); } return T; } int main() { g.mo[0][1] = 1; g.mo[1][0] = 2; g.mo[1][2] = g.mo[1][1] = 1; g.mo[1][3] = g.mo[1][4] = 3; g.mo[1][5] = g.mo[2][2] = 1; g.mo[2][3] = g.mo[2][4] = 3; g.mo[2][5] = g.mo[3][3] = 1; g.mo[3][4] = 2; g.mo[3][5] = g.mo[4][4] = g.mo[4][5] = g.mo[5][5] = 1; m.mo[0][0] = 1; m.mo[1][0] = 2; m.mo[2][0] = 8; m.mo[3][0] = 4; m.mo[4][0] = 2; m.mo[5][0] = 1; ll T,n; cin>>T; while(T--) { cin>>n; martix ans=powmo(g,n-2); ans=mul(ans,m); cout<<ans.mo[1][0]<<endl; } return 0; }","categories":[{"name":"组合数学","slug":"组合数学","permalink":"http://WigginsLi.github.io/categories/组合数学/"}],"tags":[{"name":"矩阵快速幂","slug":"矩阵快速幂","permalink":"http://WigginsLi.github.io/tags/矩阵快速幂/"},{"name":"二项式定理","slug":"二项式定理","permalink":"http://WigginsLi.github.io/tags/二项式定理/"}],"keywords":[{"name":"组合数学","slug":"组合数学","permalink":"http://WigginsLi.github.io/categories/组合数学/"}]},{"title":"hdu-6467简单数学题【数论/组合数学】","slug":"hdu-6467","date":"2019-03-18T21:56:02.000Z","updated":"2019-03-18T15:17:06.077Z","comments":true,"path":"2019/03/19/hdu-6467/","link":"","permalink":"http://WigginsLi.github.io/2019/03/19/hdu-6467/","excerpt":"$\\implies$HDU传送门 推式子题,赛场上没推出来,学习学习","text":"$\\implies$HDU传送门 推式子题,赛场上没推出来,学习学习 题意:求$$F(n) = \\sum_{i=1}^n (i \\times \\sum_{j=i}^n C_j^i)$$$mod\\,1000000007$ 第一种方法: 运用错位相减法,有$$F(n)=1\\cdot2^0+\\cdots+n\\cdot2^{n-1}\\,(1) \\\\ 2\\cdot F(n)=1\\cdot2^1+\\cdots+n\\cdot2^{n}\\,(2)$$ $ (1)-(2): $ $$ -F(n)=(1-n)\\cdot2^n-1 \\\\ \\therefore F(n)=1+(n-1)\\cdot2^n$$ 第二种方法:把式子展开得,$F(n)=$$1\\cdot(C_1^1+\\cdots+C_n^1) \\\\ + 2\\cdot(C_2^2+\\cdots+C_n^2) \\\\ \\cdots \\\\ +n\\cdot C_n^n$ 由杨辉三角的性质(可以画出图(在下面)来理解):$F(n)=1 \\cdot C_{n+1}^2+\\cdots +n\\cdot C_{n+1}^{n+1} \\\\ \\implies \\sum_{i=1}^n i\\cdot C_{n+1}^{i+1} \\\\ \\implies \\sum_{i=1}^n (i+1)\\cdot C_{n+1}^{i+1}-\\sum_{i=1}^n C_{n+1}^{i+1} \\\\ \\implies (n+1)\\sum_{i=1}^n C_{n}^i-(2^{n+1}-(n+1)-1) \\\\ \\implies (n+1)(2^n-1)-(2^{n+1}-(n+1)-1) \\\\ \\implies (n-1)\\cdot 2^n +1$ 代码: #include <bits/stdc++.h> using namespace std; #define MOD 1000000007 typedef long long ll; ll n; ll ksm(ll a,ll b) { if(b==0) return 1; ll cnt=1; cnt=ksm(a,b/2)%MOD; cnt=cnt*cnt%MOD; if(b&1) cnt=cnt*a%MOD; return cnt; } int main() { while(cin>>n) cout<<(((n-1)%MOD)*ksm(2,n)%MOD+1)%MOD<<endl; return 0; }","categories":[{"name":"数论","slug":"数论","permalink":"http://WigginsLi.github.io/categories/数论/"},{"name":"组合数学","slug":"组合数学","permalink":"http://WigginsLi.github.io/categories/组合数学/"}],"tags":[{"name":"推式子","slug":"推式子","permalink":"http://WigginsLi.github.io/tags/推式子/"}],"keywords":[{"name":"数论","slug":"数论","permalink":"http://WigginsLi.github.io/categories/数论/"},{"name":"组合数学","slug":"组合数学","permalink":"http://WigginsLi.github.io/categories/组合数学/"}]},{"title":"欧拉定理初识与应用","slug":"eulerdlksm","date":"2019-03-18T12:58:28.000Z","updated":"2019-05-27T12:52:16.813Z","comments":true,"path":"2019/03/18/eulerdlksm/","link":"","permalink":"http://WigginsLi.github.io/2019/03/18/eulerdlksm/","excerpt":"上次打gzu校赛时碰到了一道大整数幂取模的题赛后得知是欧拉定理,当场懵逼,赶紧补一下知识面","text":"上次打gzu校赛时碰到了一道大整数幂取模的题赛后得知是欧拉定理,当场懵逼,赶紧补一下知识面 欧拉定理先给出欧拉定理的一般式(证明移步百科)(~其实还是维基讲得好,有条件可以看看~) 对正整数$a,N$,若$gcd(a,N)=1$,则有$$ a^{\\varphi (N)} \\equiv 1(mod\\,N)$$其中$$\\varphi (N)=N\\cdot \\prod_{p \\mid N}\\left(\\frac{p-1}{p}\\right)$$欧拉函数$\\varphi (N)$表示小于或等于$N$的正整数中与$N$互质的数的个数 欧拉函数性质:$\\varphi (1)=1$$\\varphi (p^k)=p^k-p^{k-1}=(p-1)\\cdotp^{k-1}$$\\varphi (mn)=\\varphi (m)\\cdot\\varphi(n)$ ,其中$gcd(m,n)=1$ 特殊的,当$N$为质数时,$\\varphi (N)=N-1$,有$$ a^{N-1} \\equiv 1(mod\\,N) $$即费马小定理是欧拉定理的推广 应用:1. 求逆元 2.简化幂运算 应用 求$a^b\\,mod\\,m$,b达到100000位,m<1e6 $$ a^b\\,mod\\,m=a^{\\varphi(m)\\cdot r+s}\\,mod\\,m \\\\ \\implies ((a^{\\varphi(m)})^rmod\\,m\\cdot a^s\\,mod\\,m)\\,mod\\,m \\\\ \\implies a^s\\,mod\\,m $$","categories":[{"name":"数论","slug":"数论","permalink":"http://WigginsLi.github.io/categories/数论/"}],"tags":[{"name":"欧拉","slug":"欧拉","permalink":"http://WigginsLi.github.io/tags/欧拉/"},{"name":"快速幂","slug":"快速幂","permalink":"http://WigginsLi.github.io/tags/快速幂/"},{"name":"费马小定理","slug":"费马小定理","permalink":"http://WigginsLi.github.io/tags/费马小定理/"}],"keywords":[{"name":"数论","slug":"数论","permalink":"http://WigginsLi.github.io/categories/数论/"}]},{"title":"USACO-Team Tic Tac Toe【暴力】","slug":"rating-3-C","date":"2019-03-16T11:45:25.000Z","updated":"2019-03-16T12:00:48.341Z","comments":true,"path":"2019/03/16/rating-3-C/","link":"","permalink":"http://WigginsLi.github.io/2019/03/16/rating-3-C/","excerpt":"$\\implies$contest传送门$\\implies$USACO传送门 考察细节","text":"$\\implies$contest传送门$\\implies$USACO传送门 考察细节 大意:给一个井字棋的棋盘,判断是否有多少牛成功连成一条线,或者有两个牛组成一队,有多少队组成一条线 分析: 直接模拟暴力就好啦(开始Wa了一发因为没有判断这支队伍是不是已经统计过了) 代码: #include <bits/stdc++.h> using namespace std; string s[3]; vector<char> v; map<char,int> m; map<char,int> vt1; map<pair<char,char>,int> vt2; int main() { for(int i=0;i<3;i++) { cin>>s[i]; } int cnt=0,ans1=0,ans2=0; for(int i=0;i<3;i++) { cnt=0; m.clear(); for(int j=0;j<3;j++) { if(m.count(s[i][j])==0) { m[s[i][j]]=1; cnt++; } } //cout<<cnt<<endl; if(cnt==1) {if(vt1.count(s[i][0])==0) ans1++,vt1[s[i][0]]=1;} else if(cnt==2) { string s2=s[i]; sort(s2.begin(),s2.end()); char a = s2[0]; char b; if(s2[0]!=s2[1]) b=s2[1]; else b=s2[2]; //cout<<i<<a<<" "<<b<<endl; if(vt2.count(make_pair(a,b))==0) vt2[make_pair(a,b)]=1,ans2++; } } for(int j=0;j<3;j++) { cnt=0; m.clear(); for(int i=0;i<3;i++) { if(m.count(s[i][j])==0) { m[s[i][j]]=1; cnt++; } } //cout<<cnt<<endl; if(cnt==1) {if(vt1.count(s[0][j])==0) ans1++,vt1[s[0][j]]=1;} else if(cnt==2) { char a=s[0][j]; char b; if(a!=s[1][j]) b=s[1][j]; else b=s[2][j]; if(a>b) swap(a,b); //cout<<a<<" "<<b<<endl; if(vt2.count(make_pair(a,b))==0) vt2[make_pair(a,b)]=1,ans2++; } } cnt=0; m.clear(); for(int i=0,j=0;i<3;i++,j++) { if(m.count(s[i][j])==0) { m[s[i][j]]=1; cnt++; } } //cout<<cnt<<endl; if(cnt==1) {if(vt1.count(s[1][1])==0) ans1++,vt1[s[1][1]]=1;} else if(cnt==2) { char a=s[0][0]; char b; if(a!=s[1][1]) b=s[1][1]; else b=s[2][2]; if(a>b) swap(a,b); //cout<<a<<" "<<b<<endl; if(vt2.count(make_pair(a,b))==0) vt2[make_pair(a,b)]=1,ans2++; } cnt=0; m.clear(); for(int i=0,j=2;i<3;i++,j--) { if(m.count(s[i][j])==0) { m[s[i][j]]=1; cnt++; } } //cout<<cnt<<endl; if(cnt==1) {if(vt1.count(s[1][1])==0) ans1++,vt1[s[1][1]]=1;} else if(cnt==2) { char a=s[0][2]; char b; if(a!=s[1][1]) b=s[1][1]; else b=s[2][0]; if(a>b) swap(a,b); //cout<<a<<" "<<b<<endl; if(vt2.count(make_pair(a,b))==0) vt2[make_pair(a,b)]=1,ans2++; } cout<<ans1<<endl; cout<<ans2<<endl; return 0; }","categories":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"暴力","slug":"暴力","permalink":"http://WigginsLi.github.io/categories/暴力/"}],"tags":[{"name":"暴力","slug":"暴力","permalink":"http://WigginsLi.github.io/tags/暴力/"}],"keywords":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"暴力","slug":"暴力","permalink":"http://WigginsLi.github.io/categories/暴力/"}]},{"title":"USACO18OPEN-Out of Sorts G【思维】","slug":"rating-3-F","date":"2019-03-16T11:05:58.000Z","updated":"2019-03-16T03:37:06.822Z","comments":true,"path":"2019/03/16/rating-3-F/","link":"","permalink":"http://WigginsLi.github.io/2019/03/16/rating-3-F/","excerpt":"$\\implies$contest传送门$\\implies$洛谷传送门 out of sort系列第二题","text":"$\\implies$contest传送门$\\implies$洛谷传送门 out of sort系列第二题 大意:在out of sort S的基础上,每次进行冒泡后,再进行一次反向冒泡,求moo次数 sorted = false while (not sorted): sorted = true moo for i = 0 to N-2: if A[i+1] < A[i]: swap A[i], A[i+1] for i = N-2 downto 0: if A[i+1] < A[i]: swap A[i], A[i+1] for i = 0 to N-2: if A[i+1] < A[i]: sorted = false 分析: 首先我们先按排好序后的位置给$N$个数离散化成ta的值,比如1 8 5 3 2$\\implies$ 1 5 4 3 2 再模拟一下这个双向冒泡过程,可以发现对于每个 $i$向后扫会保证把一个在前面的$值^{(1)}$ > $i$的数移到后面向左扫会保证被换到 $i$ 前面的数的值 $\\leq$ $i$ 那么我们只需要取最大的,每个数前面有几个数的值比ta的大的数,即可 (1):此后的值均值离散后的值 代码: #include <bits/stdc++.h> using namespace std; const int N=1e5+5; struct data { int val,num; friend bool operator <(data x,data y){return x.val<y.val;} }a[N]; int n,ans=1,cnt; int vis[N]; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i].val),a[i].num=i; sort(a+1,a+n+1); for(int i=1;i<=n;i++) { if(i<a[i].num) cnt++; if(vis[i]) cnt--; vis[a[i].num]=true; ans=max(ans,cnt); } printf("%d",ans); } 后记:现在做思维题比较看运气,xjb想,没有比较规范的分析过程(该打","categories":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"思维","slug":"思维","permalink":"http://WigginsLi.github.io/categories/思维/"}],"tags":[{"name":"思维","slug":"思维","permalink":"http://WigginsLi.github.io/tags/思维/"}],"keywords":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"思维","slug":"思维","permalink":"http://WigginsLi.github.io/categories/思维/"}]},{"title":"USACO18OPEN-Out of Sorts S【思维】","slug":"rating-3-A","date":"2019-03-16T10:49:17.000Z","updated":"2019-03-16T03:13:21.153Z","comments":true,"path":"2019/03/16/rating-3-A/","link":"","permalink":"http://WigginsLi.github.io/2019/03/16/rating-3-A/","excerpt":"$\\implies$contest传送门$\\implies$洛谷传送门 out of sort系列第一题","text":"$\\implies$contest传送门$\\implies$洛谷传送门 out of sort系列第一题 大意:给出$N$个数,求冒泡排序过程排序未完成时进行了多少次第2层循环,即运行了多少次moo(觉得意思不清楚的直接看洛谷叭) sorted = false while (not sorted): sorted = true moo for i = 0 to N-2: if A[i+1] < A[i]: swap A[i], A[i+1] sorted = false 分析: 模拟一下冒泡排序的过程,可以发现每进行一次第二层循环,小的$数^{(1)}$就会向左一格,大的数却不一定只是向右一格 所以我们只要取离排序好的位置最远的后面的小的数,即为答案 (1):即给出的这个数的位置和排序好后的位置比较在后边的数,比如1,3,2,4;排好是1,2,3,4;那这个2就是小的数 代码: #include <bits/stdc++.h> using namespace std; const int maxn = 1e5+5; struct Node{ int w,id; }a[maxn]; int n; bool cmp(const Node &a,const Node &b) { return a.w<b.w; } int main() { cin>>n; for(int i=1;i<=n;i++) {cin>>a[i].w;a[i].id=i;} stable_sort(a+1,a+n+1,cmp); int ans=0; for(int i=1;i<=n;i++) { ans=max(ans,a[i].id-i); } cout<<ans+1<<endl; return 0; }","categories":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"思维","slug":"思维","permalink":"http://WigginsLi.github.io/categories/思维/"}],"tags":[{"name":"思维","slug":"思维","permalink":"http://WigginsLi.github.io/tags/思维/"}],"keywords":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"思维","slug":"思维","permalink":"http://WigginsLi.github.io/categories/思维/"}]},{"title":"USACO18FEB-Snow Boots S【线性dp】","slug":"rating-2-G","date":"2019-03-16T09:43:22.000Z","updated":"2019-03-16T03:41:20.534Z","comments":true,"path":"2019/03/16/rating-2-G/","link":"","permalink":"http://WigginsLi.github.io/2019/03/16/rating-2-G/","excerpt":"$\\implies$contest传送门$\\implies$洛谷传送门 又是一道比较简单的dp,但是赛场上还是没出_(:з」∠)","text":"$\\implies$contest传送门$\\implies$洛谷传送门 又是一道比较简单的dp,但是赛场上还是没出_(:з」∠) 大意:$N$块地砖(每块有积雪深度$f_i$),农夫需要穿靴子过去,有$M$双靴子,每双靴子最多能走在雪深$s_i$,每一步最多走$d_i$米,农夫只能按顺序穿靴子,换靴子的时候需要注意当前靴子和要换上的靴子都能踩在这一块砖上,求农夫到达$N$_th地砖时,最少需要丢弃多少双鞋。 分析: 用$dp[i]$表示$i$这块地砖能不能到达 类似多重背包求解可行性问题的解法,对每双靴子$i$,遍历地砖$j$,若$dp[j]==true$且$s[i]>f[j]$ (表示$j$这块砖之前有穿着其他靴子能到达,而且可以换上$i$这个靴子)那么$dp[k],k\\in[j,j+d[i]]=true$,(表示j后面这$b[i]$块都能到) 若$f[n]$在$i$-th鞋时可行,输出$ans=i-1$ 代码: #include<bits/stdc++.h> #define maxn 500 using namespace std; int d[maxn],b[maxn],a[maxn],n,m; bool dp[maxn]; int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) scanf("%d",&d[i]); for (int i=1;i<=m;i++) scanf("%d%d",&a[i],&b[i]); for (int i=1;i<=n;i++) dp[i]=false; dp[1]=true; int ans; for (int i=1;i<=m;i++) { for (int j=1;j<=n;j++) if (dp[j]&&(a[i]>=d[j])) { for (int k=j;k<=min(n,j+b[i]);k++) if (a[i]>=d[k]) dp[k]=true; } if (dp[n]) {ans=i;break;} } printf("%d\\n",ans-1); return 0; }","categories":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"动态规划","slug":"动态规划","permalink":"http://WigginsLi.github.io/categories/动态规划/"}],"tags":[{"name":"dp","slug":"dp","permalink":"http://WigginsLi.github.io/tags/dp/"}],"keywords":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"动态规划","slug":"动态规划","permalink":"http://WigginsLi.github.io/categories/动态规划/"}]},{"title":"USACO-Teleportation【模拟】","slug":"rating-2-F","date":"2019-03-16T01:39:34.000Z","updated":"2019-03-15T17:51:09.989Z","comments":true,"path":"2019/03/16/rating-2-F/","link":"","permalink":"http://WigginsLi.github.io/2019/03/16/rating-2-F/","excerpt":"$\\implies$ contest传送门$\\implies$ USACO传送门 水题一道,仅做记录","text":"$\\implies$ contest传送门$\\implies$ USACO传送门 水题一道,仅做记录 大意:给出a,b,x,y四个点;x和y可以互相瞬间到达,求a到b的最小距离 分析: 模拟枚举所有情况即可 代码: #include <bits/stdc++.h> using namespace std; int a,b,x,y,ans=1e4; int main() { cin>>a>>b>>x>>y; ans=min(ans,abs(a-b)); ans=min(ans,abs(a-x)+abs(y-b)); ans=min(ans,abs(a-y)+abs(x-b)); cout<<ans<<endl; return 0; }","categories":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"模拟","slug":"模拟","permalink":"http://WigginsLi.github.io/categories/模拟/"}],"tags":[{"name":"模拟","slug":"模拟","permalink":"http://WigginsLi.github.io/tags/模拟/"}],"keywords":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"模拟","slug":"模拟","permalink":"http://WigginsLi.github.io/categories/模拟/"}]},{"title":"USACO18FEB-Rest Stops【贪心】","slug":"rating-2-C","date":"2019-03-16T00:28:36.000Z","updated":"2019-03-15T17:25:25.389Z","comments":true,"path":"2019/03/16/rating-2-C/","link":"","permalink":"http://WigginsLi.github.io/2019/03/16/rating-2-C/","excerpt":"$\\implies$contest传送门$\\implies$洛谷传送门 场上没做出来的贪心题,关键是没读懂题意or2","text":"$\\implies$contest传送门$\\implies$洛谷传送门 场上没做出来的贪心题,关键是没读懂题意or2 大意:$Farmer John$和他的私人教练$Bessie$正在徒步攀登温哥牛山。基于他们的目的(也是你的目的),这座山可以用一条长为$L$米$(1 \\leq L \\leq 10^6)$的长直路径表示。$Farmer John$会沿着这条路径以每米$r_F$秒$(1 \\leq r_F \\leq 10^6)$的固定速度攀登。由于他正在训练他的耐力,他在途中不会进行任何的休息。 然而$Bessie$可以在休息站休息,在那里她能够找到一些美味的嫩草。当然,她也不能在任何地方都休息!在路径上总共有$N$个休息站$(1 \\leq N \\leq 10^5)$;第$i$个休息站距离路径的起点$x_i$米$(0 < x_i < L)$ ,美味值为$c_i(1 \\leq c_i \\leq 10^6)$。如果$Bessie$在休息站$i$休息了$t$秒,她能够得到$c_i \\cdot t$ 个美味单位。 不在休息站的时候,$Bessie$会以每米$r_B$秒$(1 \\leq r_B \\leq 10^6)$的固定速度攀登。由于$Bessie$年轻而健康,$r_B$严格小于$r_F$。 $Bessie$想要吃到最多的美味嫩草。然而她也担心$Farmer John$;她认为如果在任何时候她位于$Farmer John$身后,$Farmer John$可能就会失去前进的动力了! 帮助$Bessie$求出,在确保$Farmer John$能够完成登山的情况下,她能够获得的最多的美味单位。 分析: 比较简单的贪心策略,每次选择当前位置往后中美味值最大的休息站吃草 简单的不严谨证明:假设有两个休息站$M$,$N$,现在$Be$在$M$点处,$FJ$在$S$点处,已知$S$点到$M$点距离为$R_1$,$M$到$N$为$R_2$,如果$Be$选择在$M$点吃草还能到$N$点吃草,那么能得到的美味值是$C_M\\times r_f\\cdot R_1+(r_f-r_B)\\cdot R_2\\times C_N$,如果$Be$选择在$N$点吃草,那么能得到的美味值是$C_N\\times (r_f\\cdot (R_1+R_2)-r_B\\cdot R_2) \\implies C_N\\times (r_f\\cdot R_1 + (r_f-r_B)\\cdot R_2)$显然,在$M,N$两点吃草得到的美味值只与自身的美味值有关$\\therefore$ 选择美味值最大的休息站吃草是正确的策略 代码: #include <iostream> #include <algorithm> using namespace std; struct node { long long x; long long c; } a[100010]; bool cmp(const node &a, const node &b) { if(a.c > b.c) return true; return false; } int main() { long long l, n, f, b; cin >> l >> n >> f >> b; for(int i = 1; i <= n; i++) cin >> a[i].x >> a[i].c; long long t = f-b; sort(a+1, a+n+1, cmp); long long ans = a[1].c*(a[1].x*t), last = 1; for(int i = 2; i <= n; i++) { if(a[i].x > a[last].x) { ans += a[i].c*((a[i].x-a[last].x)*t); last = i; } } cout << ans << endl; return 0; } }","categories":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"贪心","slug":"贪心","permalink":"http://WigginsLi.github.io/categories/贪心/"}],"tags":[{"name":"贪心","slug":"贪心","permalink":"http://WigginsLi.github.io/tags/贪心/"}],"keywords":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"贪心","slug":"贪心","permalink":"http://WigginsLi.github.io/categories/贪心/"}]},{"title":"USACO-Hoofball【dfs/dp】","slug":"rating-2-B","date":"2019-03-15T23:11:44.000Z","updated":"2019-03-15T16:20:17.413Z","comments":true,"path":"2019/03/16/rating-2-B/","link":"","permalink":"http://WigginsLi.github.io/2019/03/16/rating-2-B/","excerpt":"$\\implies$contest传送门 嘛还是找不到其他题库链接这次排位赛的题就全部贴题面了 本来dfs可以做,不过赛场上没有调出来大概是求出有几个闭环,再加以计算即可 赛后看了下题解,发现有$dp$的做法,学习一波","text":"$\\implies$contest传送门 嘛还是找不到其他题库链接这次排位赛的题就全部贴题面了 本来dfs可以做,不过赛场上没有调出来大概是求出有几个闭环,再加以计算即可 赛后看了下题解,发现有$dp$的做法,学习一波 题面:In preparation for the upcoming hoofball tournament, Farmer John is drilling his $N$ cows (conveniently numbered $1…N$, where $1≤N≤100$) in passing the ball. The cows are all standing along a very long line on one side of the barn, with cow $i$ standing $x_i$ units away from the barn $(1≤x_i≤1000)$. Each cow is standing at a distinct location. At the beginning of the drill, Farmer John will pass several balls to different cows. When cow $i$ receives a ball, either from Farmer John or from another cow, she will pass the ball to the cow nearest her (and if multiple cows are the same distance from her, she will pass the ball to the cow farthest to the left among these). So that all cows get at least a little bit of practice passing, Farmer John wants to make sure that every cow will hold a ball at least once. Help him figure out the minimum number of balls he needs to distribute initially to ensure this can happen, assuming he hands the balls to an appropriate initial set of cows. InputThe first line of input contains $N$. The second line contains $N$ space-separated integers, where the $i$th integer is $x_i$. OutputPlease output the minimum number of balls Farmer John must initially pass to the cows, so that every cow can hold a ball at least once. 大意:$N$头牛互相传球,每个牛会把球传给离他最近的那个,如果距离相同,传给左边的求最小的球数使得全部的牛都有机会摸到球 分析: $dp[i][0/1]$表示第$i$个球传到右边/传到左边的最大值 $i$-th如果传到右边,可能是$i-1$-th传过来的,也可能是左边的不能传过来,所以新增一个球$i$-th如果传到左边,可能是传到$i-1$-th的,也可能是$i-1$向右不能传到$i$,所以新增一个球 预处理出ok[i][0/1]每个球是否能走到右边/左边有转移方程:$$ \\begin{cases} dp[i][0]= min(dp[i-1][0]+!(ok[i-1][0]),dp[i-1][1]+1) \\\\ dp[i][1]= min(dp[i-1][1]+!(ok[i][1]),dp[i-1][0]+1) \\end{cases}$$ 最后输出$min(dp[n][0],dp[n][1])$即可 代码: #include <bits/stdc++.h> using namespace std; const int maxn = 2e3+5; const int INF = 0x3f3f3f3f; int n,a[maxn],dp[maxn][2],ok[maxn][2]; int main() { cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; sort(a+1,a+n+1); a[0]=-1*INF; a[n+1]=INF; for(int i=1;i<=n;i++) { if(a[i+1]-a[i]<a[i]-a[i-1]) ok[i][0]=1; else ok[i][1]=1; } dp[1][0]=dp[1][1]=1; for(int i=2;i<=n;i++) { dp[i][0]=min(dp[i-1][0]+(!ok[i-1][0]),dp[i-1][1]+1); dp[i][1]=min(dp[i-1][1]+(!ok[i][1]),dp[i-1][0]+1); } cout<<min(dp[n][0],dp[n][1])<<endl; return 0; }","categories":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"动态规划","slug":"动态规划","permalink":"http://WigginsLi.github.io/categories/动态规划/"},{"name":"搜索","slug":"搜索","permalink":"http://WigginsLi.github.io/categories/搜索/"}],"tags":[{"name":"dp","slug":"dp","permalink":"http://WigginsLi.github.io/tags/dp/"}],"keywords":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"动态规划","slug":"动态规划","permalink":"http://WigginsLi.github.io/categories/动态规划/"},{"name":"搜索","slug":"搜索","permalink":"http://WigginsLi.github.io/categories/搜索/"}]},{"title":"USACO-Taming the Herd【模拟】","slug":"rating-2-A","date":"2019-03-15T22:23:31.000Z","updated":"2019-03-15T15:02:47.520Z","comments":true,"path":"2019/03/16/rating-2-A/","link":"","permalink":"http://WigginsLi.github.io/2019/03/16/rating-2-A/","excerpt":"$\\implies$contest传送门 找不到其他题库的链接,就不放了_(:з」∠)我直接贴题面了","text":"$\\implies$contest传送门 找不到其他题库的链接,就不放了_(:з」∠)我直接贴题面了 题面:Early in the morning, Farmer John woke up to the sound of splintering wood. It was the cows, and they were breaking out of the barn again! Farmer John was sick and tired of the cows’ morning breakouts, and he decided enough was enough: it was time to get tough. He nailed to the barn wall a counter tracking the number of days since the last breakout. So if a breakout occurred in the morning, the counter would be $0$ that day; if the most recent breakout was $3$ days ago, the counter would read $3$. Farmer John meticulously logged the counter every day. The end of the year has come, and Farmer John is ready to do some accounting. The cows will pay, he says! But lo and behold, some entries of his log are missing! Farmer John is confident that the he started his log on the day of a breakout. Please help him determine, out of all sequences of events consistent with the log entries that remain, the minimum and maximum number of breakouts that may have take place over the course of the logged time. InputThe first line contains a single integer $N (1≤N≤100)$, denoting the number of days since Farmer John started logging the cow breakout counter. The second line contains $N$ space-separated integers. The $i$th integer is either −1, indicating that the log entry for day $i$ is missing, or a non-negative integer $a_i$ (at most $100$), indicating that on day $i$ the counter was at $a_i$. OutputIf there is no sequence of events consistent with Farmer John’s partial log and his knowledge that the cows definitely broke out of the barn on the morning of day 1, output a single integer −1. Otherwise, output two space-separated integers m followed by $M$, where m is the minimum number of breakouts of any consistent sequence of events, and $M$ is the maximum. 大意:每当奶牛闯祸农夫就在当天记下$0$,之后的奶牛没闯祸的日子,农夫记下奶牛最近闯祸的时间差,比如奶牛$3$天前闯祸则记下$3$,后来记录被奶牛破坏了,$-1$表示当天数据损坏,输出奶牛闯祸的最大可能天数和最小可能天数如果记录和实际情况不符,输出$-1$(已知第一天奶牛一定闯祸) 分析: 先把能推断出来的数据恢复(注意判断是否有非法情况) 最小的可能天数即当前得到的数据里面$0$的个数最大的可能天数为当前得到数据里面$0$和$-1$的个数 代码: #include <bits/stdc++.h> using namespace std; const int maxn = 105; int n,a[maxn]; int main() { cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=n-1;i>=1;i--) { if(a[i+1]!=-1&&a[i+1]!=0) { if(a[i]==-1) a[i]=a[i+1]-1; else if(a[i]!=a[i+1]-1) { cout<<-1<<endl; return 0; } } } if(a[1]==-1) a[1]=0; if(a[1!=0]) { cout<<-1<<endl; return 0; } int m=0,M=0; for(int i=1;i<=n;i++) { if(a[i]==0) { m++;M++; }else if(a[i]==-1) { M++; } } cout<<m<<" "<<M<<endl; return 0; }","categories":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"模拟","slug":"模拟","permalink":"http://WigginsLi.github.io/categories/模拟/"}],"tags":[{"name":"模拟","slug":"模拟","permalink":"http://WigginsLi.github.io/tags/模拟/"}],"keywords":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"模拟","slug":"模拟","permalink":"http://WigginsLi.github.io/categories/模拟/"}]},{"title":"USACO18DEC-Convention II【模拟+优先队列】","slug":"rating-1-I","date":"2019-03-15T18:38:08.000Z","updated":"2019-03-15T11:27:12.411Z","comments":true,"path":"2019/03/16/rating-1-I/","link":"","permalink":"http://WigginsLi.github.io/2019/03/16/rating-1-I/","excerpt":"$\\implies$ contest传送门$\\implies$ 洛谷传送门 第一次排位赛的最后一道必补题(都是水题,场上没能A完真是失败_(:з」∠)","text":"$\\implies$ contest传送门$\\implies$ 洛谷传送门 第一次排位赛的最后一道必补题(都是水题,场上没能A完真是失败_(:з」∠) 大意:N头牛排队吃草,每头牛有到达时间$a_i$,需要$t_i$时间吃草如果当前已经有牛在吃草,那么开始排队,排队的顺序不按时间顺序而是按每头牛的资历(按输入顺序)从大到小排序输出所有奶牛中在队伍里等待的时间($a_i$到这头奶牛开始吃草之间的时间)的最大值。 分析: 维护两个优先队列$arr$和$wait$分别模拟到达的队列和等待队列 每个牛有状态$<s_1,s_2,s_3,s_4>$分别表示到达时间,牛的资历,吃的时长,开始等待的时间($s_1和s_2$在两队列互换位置,目的是$arr$队列按时间排序,$wait$队列按资历排序) 模拟出队步骤,记录好每头开始吃草的牛开始排队时间和当前时间的差,取最大值 输出即可 代码: #include <bits/stdc++.h> using namespace std; typedef pair<int,int> pii; typedef pair<pii,pii> piii; priority_queue<piii,vector<piii>,greater<piii> > arr,wait; int ans=-1,n,now; int main() { cin>>n; for(int i=1;i<=n;i++) { int a,t; cin>>a>>t; arr.push(piii(pii(a,i),pii(t,-1))); } now=0; while(!arr.empty()||!wait.empty()) { if(wait.empty()) { piii x = arr.top();arr.pop(); swap(x.first.first,x.first.second); now=x.first.second; wait.push(x); } while(!wait.empty()) { piii x = wait.top();wait.pop(); ans=max(ans,now-x.first.second); x.second.second=now; now+=x.second.first; while(!arr.empty()&&arr.top().first.first<=now) { piii u=arr.top();arr.pop(); swap(u.first.first,u.first.second); wait.push(u); } } } cout<<ans<<endl; return 0; }","categories":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"数据结构","slug":"数据结构","permalink":"http://WigginsLi.github.io/categories/数据结构/"},{"name":"模拟","slug":"模拟","permalink":"http://WigginsLi.github.io/categories/模拟/"}],"tags":[],"keywords":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"数据结构","slug":"数据结构","permalink":"http://WigginsLi.github.io/categories/数据结构/"},{"name":"模拟","slug":"模拟","permalink":"http://WigginsLi.github.io/categories/模拟/"}]},{"title":"USACO18DEC-Mixing Milk【模拟】","slug":"rating-1-H","date":"2019-03-15T18:14:27.000Z","updated":"2019-03-15T10:31:52.918Z","comments":true,"path":"2019/03/16/rating-1-H/","link":"","permalink":"http://WigginsLi.github.io/2019/03/16/rating-1-H/","excerpt":"$\\implies$contest传送门$\\implies$洛谷传送门","text":"$\\implies$contest传送门$\\implies$洛谷传送门 大意:有三个桶,分别有容量$C_i$和初始水量$M_i$从一个桶倒入另一个桶算作一次倒水操作现在从桶$1$倒入桶$2$,桶$2$倒入桶$3$,桶$3$倒入桶$1$,重复这个过程,直到$100$次倒水操作后输出桶$1$的剩余水量 分析: 嘛~随便模拟下就好啦这个题解就裸奔了 代码: #include <bits/stdc++.h> using namespace std; typedef long long ll; ll ma,mb,mc; ll a,b,c; int main() { cin>>ma>>a; cin>>mb>>b; cin>>mc>>c; for(int i=1;i<=33;i++) { if(a+b>mb) { a=a+b-mb; b=mb; }else{ b=a+b; a=0; } if(b+c>mc) { b=b+c-mc; c=mc; }else{ c=b+c; b=0; } if(c+a>ma) { c=c+a-ma; a=ma; }else{ a=c+a; c=0; } } if(a+b>mb) { a=a+b-mb; b=mb; }else{ b=a+b; a=0; } cout<<a<<endl<<b<<endl<<c<<endl; return 0; }","categories":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"模拟","slug":"模拟","permalink":"http://WigginsLi.github.io/categories/模拟/"}],"tags":[{"name":"模拟","slug":"模拟","permalink":"http://WigginsLi.github.io/tags/模拟/"}],"keywords":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"模拟","slug":"模拟","permalink":"http://WigginsLi.github.io/categories/模拟/"}]},{"title":"USACO18DEC-Back and Forth【dfs】","slug":"rating-1-G","date":"2019-03-15T17:48:25.000Z","updated":"2019-03-15T10:12:38.030Z","comments":true,"path":"2019/03/16/rating-1-G/","link":"","permalink":"http://WigginsLi.github.io/2019/03/16/rating-1-G/","excerpt":"$\\implies$contest传送门$\\implies$洛谷传送门 比较简单的暴力题(刷太多水题了啊orz","text":"$\\implies$contest传送门$\\implies$洛谷传送门 比较简单的暴力题(刷太多水题了啊orz 大意:农夫有两个房间,分别有$1$个大水缸和$10$个不定容量的桶周一农夫在两水缸分别倒入1000加仑牛奶,周二随机挑一个(在第一个房间的)桶从第一个水缸装满牛奶运到第二个水缸,并把水桶留下。周三反过来从第二个水缸运牛奶到第一个水缸周四周五重复周二周三的动作问周末时第一个水缸的牛奶的量有多少种可能性 分析: 设状态$(C,lv)$表示当前是周$lv$,第一个水缸剩余奶量$C$ 以(1000,1)开始dfs暴搜,选择当前房间的每个桶继续递归搜索,直到周五记录当前剩余奶量 统计答案个数即可 代码: #include <bits/stdc++.h> using namespace std; const int maxn = 1e3+5; int a[maxn],b[maxn],vis[2005],ans=0; int dfs(int cnt,int lv) { if(lv==5) { if(vis[cnt]==0) ans++; vis[cnt]=1; return 0; } if(lv%2==1) { for(int i=1;i<=20;i++) { if(a[i]!=0) { if(cnt-a[i]<0) return 0; b[i]=a[i]; a[i]=0; dfs(cnt-b[i],lv+1); a[i]=b[i]; b[i]=0; } } }else { for(int i=1;i<=20;i++) { if(b[i]!=0) { if(cnt+b[i]>2000) return 0; a[i]=b[i]; b[i]=0; dfs(cnt+a[i],lv+1); b[i]=a[i]; a[i]=0; } } } return 0; } int main() { for(int i=1;i<=10;i++) cin>>a[i]; for(int i=11;i<=20;i++) cin>>b[i]; memset(vis,0,sizeof(vis)); dfs(1000,1); cout<<ans<<endl; return 0; }","categories":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"搜索","slug":"搜索","permalink":"http://WigginsLi.github.io/categories/搜索/"}],"tags":[{"name":"dfs","slug":"dfs","permalink":"http://WigginsLi.github.io/tags/dfs/"}],"keywords":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"搜索","slug":"搜索","permalink":"http://WigginsLi.github.io/categories/搜索/"}]},{"title":"USACO18DEC-Convention【二分】","slug":"rating-1-E","date":"2019-03-15T13:55:30.000Z","updated":"2019-03-15T09:47:12.335Z","comments":true,"path":"2019/03/15/rating-1-E/","link":"","permalink":"http://WigginsLi.github.io/2019/03/15/rating-1-E/","excerpt":"$\\implies$传送门(只有group里的人能进)$\\implies$洛谷传送门 二分板子题叭","text":"$\\implies$传送门(只有group里的人能进)$\\implies$洛谷传送门 二分板子题叭 大意:$N$头牛在不同时间到达机场后,农夫用卡车想把它们运回农场农夫有$M$辆车,每辆车最多可以装$C$头牛(保证$M \\times C\\geq N$),问牛最小的最大等待时间 分析: 对牛的最大等待时间进行二分,如果当前最大等待时间可行,向左边界缩进,否则反之 代码: #include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5+5; ll n,m,c,t[maxn]; int fun(int x) { int inx=1,cnt=0; while(inx<=n) { for(int i=1;i<c;i++) { if(t[inx+i]-t[inx]>x) {inx=inx+i;cnt++;break;} if(i==c-1) {inx=inx+c;cnt++;} } } if(cnt<=m) return 0; else return 1; } int main() { cin>>n>>m>>c; for(int i=1;i<=n;i++) { cin>>t[i]; } sort(t+1,t+n+1); int l=0,r=1e9,mid; while(r-l>1) { mid=(l+r)/2; //cout<<l<<" "<<r<<endl; if(fun(mid)) { l=mid; }else{ r=mid; } } if(!fun(l)) cout<<l<<endl; else if(!fun((l+r)/2)) cout<<(l+r)/2<<endl; else cout<<r<<endl; return 0; }","categories":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"二分","slug":"二分","permalink":"http://WigginsLi.github.io/categories/二分/"}],"tags":[{"name":"二分","slug":"二分","permalink":"http://WigginsLi.github.io/tags/二分/"}],"keywords":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"二分","slug":"二分","permalink":"http://WigginsLi.github.io/categories/二分/"}]},{"title":"USACO18DEC-Mooyo Mooyo【dfs+模拟】","slug":"rating-1-C","date":"2019-03-15T13:35:08.000Z","updated":"2019-03-15T06:10:31.502Z","comments":true,"path":"2019/03/15/rating-1-C/","link":"","permalink":"http://WigginsLi.github.io/2019/03/15/rating-1-C/","excerpt":"$\\implies$传送门(只有group里的人能进)$\\implies$洛谷传送门","text":"$\\implies$传送门(只有group里的人能进)$\\implies$洛谷传送门 大意:给定一个$n\\times m$的图,相邻的相同的数构成数块,当数块中数的个数≥$k$时,该数块被消除,消除所有可消除的数块。全部剩余的数块”下落”,重复消除过程,输出最终结果 分析: $dfs$求一下连通块,把所有的连通块消除掉 然后模拟一下下落过程,(因为数据比较小,瞎搞搞也能过_(:з」∠) 代码: #include <bits/stdc++.h> using namespace std; const int maxn = 1e3+5; int n,k,vis[maxn][11],cnt,num[maxn*10]; string m[maxn]; void dfs(int x,int y) { num[cnt]++; int dir[5]={0,1,0,-1,0}; for(int i=0;i<4;i++) { int nx=x+dir[i],ny=y+dir[i+1]; if(nx>=1&&ny>=0&&nx<=n&&ny<10&&!vis[nx][ny]&&m[nx][ny]==m[x][y]) { vis[nx][ny]=cnt; dfs(nx,ny); } } } int main() { cin>>n>>k; for(int i=1;i<=n;i++) { cin>>m[i]; } bool ok; while(1) { ok=1; memset(vis,0,sizeof(vis)); memset(num,0,sizeof(num)); cnt=0; for(int i=1;i<=n;i++) { int len=m[i].length(); for(int j=0;j<len;j++) { if(!vis[i][j]&&m[i][j]!='0') { cnt++; vis[i][j]=cnt; dfs(i,j); if(num[cnt]>=k) { num[cnt]=-1; ok=0; } } } } for(int i=1;i<=n;i++) { for(int j=0;j<10;j++) { if(num[vis[i][j]]==-1) { m[i][j]='0'; } } } for(int i=n;i>=1;i--) { for(int j=0;j<10;j++) { if(m[i][j]=='0') continue; int u=i+1; while(m[u][j]=='0') { u++; } if(u==i+1) continue; m[u-1][j]=m[i][j]; m[i][j]='0'; } } if(ok) break; } for(int i=1;i<=n;i++) { cout<<m[i]<<endl; } cout<<endl; return 0; }","categories":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"模拟","slug":"模拟","permalink":"http://WigginsLi.github.io/categories/模拟/"},{"name":"搜索","slug":"搜索","permalink":"http://WigginsLi.github.io/categories/搜索/"}],"tags":[{"name":"dfs","slug":"dfs","permalink":"http://WigginsLi.github.io/tags/dfs/"}],"keywords":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"模拟","slug":"模拟","permalink":"http://WigginsLi.github.io/categories/模拟/"},{"name":"搜索","slug":"搜索","permalink":"http://WigginsLi.github.io/categories/搜索/"}]},{"title":"USACO18DEC-Teamwork【简单DP】","slug":"rating-1-B","date":"2019-03-15T00:52:36.000Z","updated":"2019-03-15T05:59:32.437Z","comments":true,"path":"2019/03/15/rating-1-B/","link":"","permalink":"http://WigginsLi.github.io/2019/03/15/rating-1-B/","excerpt":"$\\implies$传送门(只有group里的人能进)$\\implies$洛谷传送门 比较简单的$dp$,但是比赛时没搞出来(:з」∠)","text":"$\\implies$传送门(只有group里的人能进)$\\implies$洛谷传送门 比较简单的$dp$,但是比赛时没搞出来(:з」∠) 大意:$N$个数组成的序列,分成数个连续的子序列,每个子序列不超过$k$个数,子序列内每个数被修改为该序列内数的最大值,求整个序列的和最大值 分析: $dp[i]$表示第$i$个数时所能达到的最大序列和 对于当前数字而言,可以选择自己组队,和前面一个人组队,$\\cdots$,和前面$k$个人组队,那么就有$k$种状态可以转移,得到转移方程:$$ dp[i]=max(d[i-j-1]+max(a[m])\\times(j+1)),其中j\\in[0,k),m\\in[i-j,i]$$ 代码: #include <bits/stdc++.h> #define maxn 10005 using namespace std; int dp[maxn]; int a[maxn]; int n,k; int main() { scanf("%d%d",&n,&k); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); } for(int i=1;i<=n;i++){ int maxx=a[i]; dp[i]=dp[i-1]+a[i]; for(int j=1;j<k;j++){ if(i-j<=0) break; maxx=max(maxx,a[i-j]); dp[i]=max(dp[i],dp[i-j-1]+maxx*(j+1)); } } cout<<dp[n]<<endl; } 后记:$dp$的题还是练得少,这么简单的转移都没有想到 其次,对$dp$还是怕,不敢做,这也是出不了的原因","categories":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"动态规划","slug":"动态规划","permalink":"http://WigginsLi.github.io/categories/动态规划/"}],"tags":[{"name":"DP","slug":"DP","permalink":"http://WigginsLi.github.io/tags/DP/"}],"keywords":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"动态规划","slug":"动态规划","permalink":"http://WigginsLi.github.io/categories/动态规划/"}]},{"title":"USACO18DEC-The Bucket List【模拟】","slug":"rating-1-A","date":"2019-03-15T00:27:19.000Z","updated":"2019-03-15T05:59:00.053Z","comments":true,"path":"2019/03/15/rating-1-A/","link":"","permalink":"http://WigginsLi.github.io/2019/03/15/rating-1-A/","excerpt":"$\\implies$传送门(只有group里的人能进)$\\implies$洛谷传送门 一道模拟水题,仅做记录","text":"$\\implies$传送门(只有group里的人能进)$\\implies$洛谷传送门 一道模拟水题,仅做记录 大意:农夫用桶装牛奶,$cow_i$在$s_i$到$t_i$时间内挤奶需要$b_i$个桶,农夫开始有0个桶,需要多的桶可以购置,求挤完奶最少需要多少桶。 分析: 一道简单模拟题 以变量$lef$表示当前时间点农夫剩下几个(已用过但现在闲置的)桶. 记录时间轴上每个时间点是否有牛开始/结束挤奶.若开始挤奶且剩下桶不足,那么购置新的桶,若结束挤奶,那么$lef$加上该牛还回来的桶数。 最后输出$lef$即可 代码: #include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 105; int n,lef; int s[1005],t[1005],b[1005]; int main() { cin>>n; for(int i=1;i<=n;i++) { int ss,tt,bb; cin>>ss>>tt>>bb; s[ss]=i; t[tt]=i; b[i]=bb; } lef=0; for(int i=1;i<=1000;i++) { if(s[i]!=0) { if(lef >= b[s[i]]) { lef-=b[s[i]]; }else{ lef=0; } } if(t[i]!=0) { lef+=b[t[i]]; } //if(i<=15) cout<<lef<<endl; } cout<<lef<<endl; return 0; }","categories":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"模拟","slug":"模拟","permalink":"http://WigginsLi.github.io/categories/模拟/"}],"tags":[{"name":"模拟","slug":"模拟","permalink":"http://WigginsLi.github.io/tags/模拟/"}],"keywords":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"模拟","slug":"模拟","permalink":"http://WigginsLi.github.io/categories/模拟/"}]},{"title":"待补 < 树状数组+线段树 > 2019 GDUT Winter Training V","slug":"algorithm-optimize-2","date":"2019-03-13T11:35:54.000Z","updated":"2019-03-15T05:31:17.062Z","comments":true,"path":"2019/03/13/algorithm-optimize-2/","link":"","permalink":"http://WigginsLi.github.io/2019/03/13/algorithm-optimize-2/","excerpt":"$\\implies$ 专题地址寒假专题的最后一篇,主要记录一下树状数组的应用及进阶操作 还有线段树的基础苦逼肝专题的过程中,大佬已经刷起其他题库了(:з」∠)加油啊ヾ(◍°∇°◍)ノ゙","text":"$\\implies$ 专题地址寒假专题的最后一篇,主要记录一下树状数组的应用及进阶操作 还有线段树的基础苦逼肝专题的过程中,大佬已经刷起其他题库了(:з」∠)加油啊ヾ(◍°∇°◍)ノ゙ 树状数组$\\implies$ 入门资料 lowbit的字面理解上可能会有点小困难,模拟一下就好懂多了 A:Stars 大意:按y从小到大的顺序给出n个点的坐标,记录每个点左下方点的个数$x$,最后输出每个个数$x$的点的数目 用$a[i]$表示$x=i$的点的个数,那么对于点$(x_i,y_i)$,答案即为$\\sum_{i=0}^{x_i-1} a[i]$ 树状数组的应用–求和 View Codec++ #include <cstdio> #include <algorithm> #include <iostream> #include <string.h> #include <vector> using namespace std; #define lowbit(i) i&(-i) #define MAXX 32005 #define MAXN 15005 const int INf=0x3f3f3f3f; int a[MAXX],c[MAXX],level[MAXN]; int n,x,y; void add_val(int x,int p) { for(int i=x;i<=MAXX;i+=lowbit(i)) c[i]+=p; } int get_sum(int x) { int res = 0; for(int i=x;i;i-=lowbit(i)) res+=c[i]; return res; } int main() { memset(level,0,sizeof(level)); memset(a,0,sizeof(a)); memset(c,0,sizeof(c)); scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d %d",&x,&y); int s=get_sum(x+1); level[s]++; //cout<<s<<endl; add_val(x+1,1); } for(int i=0;i<n;i++) { printf("%d\\n",level[i]); } return 0; } B:A Simple Problem with Integers 大意:n个数字,给定Q个区间和查询或区间值修改,输出每次查询的值 其实应该是一道线段树模板题,但是两个树状数组的进阶操作也能做,那就学一下吧(╹▽╹) 使$d[i]$的前缀和等于$a[i]$,即$\\sum_{i=0}^nd[i]=a[n]$,那么当需要修改$[l,r]$区间时,只需要$d[l]+p,d[r+1]-p$即可 修改的问题解决了,但是查询的问题应该如何解决?注意到$$ \\begin{cases} a_n=d_1+d_2+\\cdots+d_{n-1}+d_n \\\\ a_{n-1}=d_1+d_2+\\cdots+d_{n-1} \\\\ \\cdots \\\\ a_1=d_1 \\end{cases}$$有(相当于矩形减去右下角的三角)$$\\sum_{i=1}^na[i]=\\sum_{i=1}^nd[i]\\times(x+1)-\\sum_{i=2}^nd[i]\\times i$$ 所以我们可以构造两个树状数组,一个维护$d[i]$,一个维护$d[i]\\times i$ 至此,已解决区间修改及区间查询操作 View Codec++ #include <cstdio> #include <iostream> #include <algorithm> #include <string.h> using namespace std; #define lowbit(i) i&(-i) typedef long long ll; const int MAXN = 1e5+5; ll a[MAXN],d[MAXN],id[MAXN]; int n,q; void add_val(int x,ll p) { for(int i=x;i<=n;i+=lowbit(i)) d[i]+=p,id[i]+=p*x; } ll get_sum(int x) { ll res=0; for(int i=x;i;i-=lowbit(i)) res+=d[i]*(x+1)-id[i]; return res; } int main() { scanf("%d %d",&n,&q); for(int i=1;i<=n;i++) scanf("%lld",&a[i]),add_val(i,a[i]-a[i-1]); while(q--) { char op; int l,r,p; getchar(); scanf("%c",&op); if(op == 'Q') { scanf("%d %d",&l,&r); printf("%lld\\n",get_sum(r)-get_sum(l-1)); }else { scanf("%d %d %d",&l,&r,&p); add_val(l,p); add_val(r+1,-p); } } return 0; } 线段树I:Count Color 大意:给定一长度为L的线段,初始颜色全为1,有两种操作,C a b c表示$[a,b]$段染成颜色c,P a b表示统计$[a,b]$内的颜色数量,并输出 线段树板子题 可以用位运算进行优化染色过程取一32位的二进制数,第i位表示是否染上了i这种颜色 代码中有一个黑科技求一个二进制数中1的个数,(来源:Matrix67大神的博客) View Codec++ #include <cstdio> #include <string.h> #include <iostream> #include <algorithm> #include <cmath> #define lowbit(i) i&(-i) using namespace std; typedef long long ll; const int maxn=1e5+5; int flag[4*maxn],tree[4*maxn]; inline int c1(int x) { x=(x & 0x55555555) + ((x >>1 ) & 0x55555555); x=(x & 0x33333333) + ((x >>2 ) & 0x33333333); x=(x & 0x0F0F0F0F) + ((x >>4 ) & 0x0F0F0F0F); x=(x & 0x00FF00FF) + ((x >>8 ) & 0x00FF00FF); x=(x & 0x0000FFFF) + ((x >>16) & 0x0000FFFF); return x; } void build_tree(int o,int l,int r) { flag[o]=0; tree[o]=(1<<1);//代表染上1这个颜色 if(l==r) return ; int mid = (l+r) >> 1; build_tree(o<<1,l,mid); build_tree(o<<1|1,mid+1,r); } void down(int o) { if(flag[o]) { flag[o<<1]=flag[o<<1|1]=flag[o]; tree[o<<1]=tree[o<<1|1]=tree[o]; flag[o]=0; } } void update(int o,int l,int r,int tl,int tr,int val) { if(tl<=l&&r<=tr) { flag[o]=val; tree[o]=(1<<val); return ; } down(o); int mid = (l+r) >> 1; if(tr<=mid) update(o<<1,l,mid,tl,tr,val); else if(tl>mid) update(o<<1|1,mid+1,r,tl,tr,val); else { update(o<<1,l,mid,tl,tr,val); update(o<<1|1,mid+1,r,tl,tr,val); } tree[o]=tree[o<<1]|tree[o<<1|1]; //cout<<o<<" "<<tree[o]<<endl; } int get_sum(int o,int l,int r,int tl,int tr) { if(tl<=l&&r<=tr) return tree[o]; down(o); int mid = (l+r) >>1; if(tr<=mid) return get_sum(o<<1,l,mid,tl,tr); else if(tl>mid) return get_sum(o<<1|1,mid+1,r,tl,tr); else { int t1=get_sum(o<<1,l,mid,tl,tr); int t2=get_sum(o<<1|1,mid+1,r,tl,tr); return t1|t2; } } int main() { int n,t,Q; scanf("%d %d %d",&n,&t,&Q); build_tree(1,1,n); while(Q--) { char op; int l,r,p; getchar(); scanf("%c",&op); if(op=='C') { scanf("%d %d %d",&l,&r,&p); if(l>r) swap(l,r); update(1,1,n,l,r,p); }else { scanf("%d %d",&l,&r); if(l>r) swap(l,r); int ans=get_sum(1,1,n,l,r); //cout<<ans<<endl; printf("%d\\n",c1(ans)); } } } H:Potted Flower 大意:待补","categories":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"数据结构","slug":"数据结构","permalink":"http://WigginsLi.github.io/categories/数据结构/"}],"tags":[{"name":"树状数组","slug":"树状数组","permalink":"http://WigginsLi.github.io/tags/树状数组/"},{"name":"线段树","slug":"线段树","permalink":"http://WigginsLi.github.io/tags/线段树/"}],"keywords":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"数据结构","slug":"数据结构","permalink":"http://WigginsLi.github.io/categories/数据结构/"}]},{"title":"< 单调栈+单调队列+矩阵快速幂 > 2019 GDUT Winter Training V","slug":"algorithm-optimize-1","date":"2019-03-13T11:32:21.000Z","updated":"2019-08-09T11:06:49.661Z","comments":true,"path":"2019/03/13/algorithm-optimize-1/","link":"","permalink":"http://WigginsLi.github.io/2019/03/13/algorithm-optimize-1/","excerpt":"$\\implies$ 专题地址算法很大一部分是通过各种数据结构加以优化的嘛有时候明明想到接近正解,结果没有优化就T了还是蛮可惜的这个专题初步接触一些普遍的数据结构","text":"$\\implies$ 专题地址算法很大一部分是通过各种数据结构加以优化的嘛有时候明明想到接近正解,结果没有优化就T了还是蛮可惜的这个专题初步接触一些普遍的数据结构 单调栈 设想一个情景,一个数列a,对于$i \\in [0,n]$,$d[i]$表示右边第一个小于$a[i]$的数的坐标,暴力做法是$O(n^2)$,运用单调栈可以优化至$O(n)^{(1)}$ (1):维护一个栈,将数列从右往左压进栈.当压入一个数$n$时,如果栈顶元素大于$n$,出栈,重复这个判断,直到栈顶元素小于$n$.那么根据这个过程,此时的栈顶元素即为n右边第一个小于ta的数,记录$d[i]$即可(左右方向,大于小于同理可得) C:Feel Good 大意:给定n个数组成序列,求一段区间,使区间和*区间内最小值的值ans最大,求MAX ans$ n \\in [1,10^5] $ 如果暴力求,可能需要$O(n^3)$ 用单调栈预处理出每个数$i$,左边第一个小于ta的数的下标$l[i]$,右边第一个小于ta的数下标$r[i]$这样可以保证$l[i]-r[i]$区间内i最小,那么只需要遍历$i$,求$ans=max(sum[l[i]-1,r[i]]*i)$即可 View Codec++ #include <cstdio> #include <iostream> #include <algorithm> #include <string.h> #include <stack> #include <vector> using namespace std; typedef long long ll; const int INF = 0x3f3f3f3f; const int MAXN = 1e5+5; int n; ll a[MAXN],sum[MAXN],l[MAXN],r[MAXN]; stack<int> s; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%lld",&a[i]); sum[i]=sum[i-1]+a[i]; } a[0]=-1;a[n+1]=-1; s.push(0); for(int i=1;i<=n;i++) { while(a[s.top()]>=a[i]) s.pop(); l[i]=s.top(); s.push(i); } while(!s.empty()) s.pop(); s.push(n+1); for(int i=n;i>=1;i--) { while(a[s.top()]>=a[i]) s.pop(); r[i]=s.top(); s.push(i); } ll ans=-1; int index=0; for(int i=1;i<=n;i++) { ll u = a[i]*(sum[r[i]-1]-sum[l[i]]); if(u>ans) { ans=u; index=i; } } cout<<ans<<endl; cout<<l[index]+1<<" "<<r[index]-1<<endl; return 0; } L:Largest Rectangle in a Histogram 大意:一列矩形,宽度固定为1,高度为$h[i]$,求能构成的最大矩形的面积 比C题更裸的单调栈,按该题思路即可 $ans=max((r[i]-1-l[i])*h[i])$ View Codec++ #include <cstdio> #include <iostream> #include <algorithm> #include <string.h> #include <stack> #include <vector> using namespace std; typedef long long ll; const int INF = 0x3f3f3f3f; const int MAXN = 1e5+5; int n; ll a[MAXN],sum[MAXN],l[MAXN],r[MAXN]; stack<int> s; int main() { while(~scanf("%d",&n)&&n) { for(int i=1;i<=n;i++) { scanf("%lld",&a[i]); sum[i]=sum[i-1]+a[i]; } a[0]=-1;a[n+1]=-1; s.push(0); for(int i=1;i<=n;i++) { while(a[s.top()]>=a[i]) s.pop(); l[i]=s.top(); s.push(i); } while(!s.empty()) s.pop(); s.push(n+1); for(int i=n;i>=1;i--) { while(a[s.top()]>=a[i]) s.pop(); r[i]=s.top(); s.push(i); } ll ans=-1; for(int i=1;i<=n;i++) { ll u = a[i]*(r[i]-l[i]-1); if(u>ans) { ans=u; } } cout<<ans<<endl; //cout<<l[index]+1<<" "<<r[index]-1<<endl; } return 0; } 单调队列 单调队列保证队列内的单调性,配合一些需要这些性质的算法,比如LIS往往有奇效 G:Sliding 大意:n个数的序列,将一个可以包含k个元素的“窗口”置于序列头,取“窗口”内的最大值和最小值,窗口每次向后滑行一格,重复取最大值、最小值,直到到达序列尾 单调队列的入门题。 以取最大值为例,维护一个队列,将序列从左往右排进队列,当前元素大于等于队尾元素时,队尾元素出队,重复该过程,直到当前元素小于队尾,插入队尾,判断队头元素下标是否在滑窗内,不是就出队头,当前队列头即为当前滑窗内最大值。 View Codec++ #include <cstdio> #include <iostream> #include <algorithm> #include <string.h> #include <vector> #include <cmath> using namespace std; typedef long long ll; const int MAXN = 1e6+5; const int INF = 0x3f3f3f3f; int n,k,a[MAXN]; int front,tail,q[MAXN]; int main() { scanf("%d %d",&n,&k); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); } front=tail=0; for(int i=1;i<=n;i++) { while(front<tail&&a[q[tail-1]]>=a[i]) tail--; q[tail++]=i; while(q[tail-1]-q[front]+1>k) front++; if(i>=k) cout<<a[q[front]]<<" "; } cout<<endl; front=tail=0; for(int i=1;i<=n;i++) { while(front<tail&&a[q[tail-1]]<=a[i]) tail--; q[tail++]=i; while(q[tail-1]-q[front]+1>k) front++; if(i>=k) cout<<a[q[front]]<<" "; } cout<<endl; return 0; } 优先队列(乱入的==)F:Expedition 大意:奶牛要到$L$米外的村庄去,带着$r$升汽油,路上有$n$个加油站,问奶牛最少要在几个加油站加油 贪心策略(每次挑已经走过的路上的油最多的加油站) 不严谨推理:当奶牛的车在路上某个点x没油时,假设奶牛在前面某个加油站加了油,我们知道奶牛本身的油量到x点时刚好耗光,那奶牛在x这个点时的油量就全部来自ta加油的那个加油站.那这时选最多油的加油站的策略显然正确。 那么我们可以借助优先队列得到走过的最多油的加油站。 View Codec++ #include <iostream> #include <algorithm> #include <cstdio> #include <queue> using namespace std; const int maxn = 1e6+5; int v[maxn],a[maxn],b[maxn]; int n,way,r,cnt=0; priority_queue<int> p; int main(){ cin>>n; for(int i=0;i<n;i++) cin>>a[i]>>b[i]; cin>>way>>r; for(int i=0;i<n;i++) v[way-a[i]]=b[i]; for(int i=0,j=r;j>=0;i++,j--){ if(i>=way){ cout<<cnt<<endl; return 0; } if(v[i]) p.push(v[i]); if(!p.empty()&&j==0){ j+=p.top(); p.pop(); cnt++; } } cout<<"-1"<<endl; return 0; } 矩阵快速幂 矩阵快速幂,即普通实数的快速幂通过重载实现得到。 当我们构造出递推方程组时可以用线代的方法构造出矩阵来运算 D:Problem of Precision 大意:如图 属于一类题 化简 $$(\\sqrt{2}+\\sqrt{3})^2=(5+2\\sqrt{6})$$ 有式子$$a_n+b_n\\sqrt{6}=(a_{n-1}+b_{n-1}\\sqrt{6}) \\times (5+2\\sqrt{6})$$再化简 $$5a_{n-1} + 12b_{n-1} + (2a_{n-1}+5b_{n-1})\\sqrt{6}=a_{n}+b_{n}\\sqrt{6}$$$$ \\implies \\begin{cases} 5a_{n-1}+12b_{n-1}=a_n \\\\ 2a_{n-1}+5b_{n-1}=b_n \\\\ \\end{cases}$$$$ \\implies \\begin{bmatrix} 5 & 12 \\\\ 2 & 3 \\\\ \\end{bmatrix} \\begin{bmatrix} a_{n-1} \\\\ b_{n-1} \\\\ \\end{bmatrix}= \\begin{bmatrix} a_n \\\\ b_n \\\\ \\end{bmatrix}$$那么我们知道最终结果为$a_n+b_n\\sqrt{6},其中\\begin{bmatrix} a_n \\\\ b_n \\\\ \\end{bmatrix}=\\begin{bmatrix} 5 & 12 \\\\ 2 & 3 \\\\ \\end{bmatrix}^n \\times \\begin{bmatrix} a_1 \\\\ b_1 \\\\ \\end{bmatrix}$,$a_1=5,b_1=2$难点!!! $$\\because (5+2\\sqrt{6})^n=a_n+b_n\\sqrt{6}, \\\\ (5-2\\sqrt{6})^n=a_n-b_n\\sqrt{6},\\\\ \\therefore (5+2\\sqrt{6})^n+(5-2\\sqrt{6})^n=2\\times a_n \\\\ \\because (5-2\\sqrt{6})^n<0.102 \\\\ \\therefore \\lfloor (5+2\\sqrt{6})^n \\rfloor=2 \\times a_n-1$$ 证毕 套用矩阵快速幂模板(具体看代码) View Codec++ #include <cstdio> #include <string.h> #include <cmath> #include <iostream> #include <algorithm> #include <vector> #include <queue> using namespace std; const int mod=1024; struct martix{ int mo[3][3]; martix(){ memset(mo,0,sizeof(mo)); } }; martix q; martix mul(martix a,martix b) { martix c; for(int i=0;i<2;i++) { for(int j=0;j<2;j++) { for(int k=0;k<2;k++) { c.mo[i][j]=(c.mo[i][j]+a.mo[i][k]*b.mo[k][j])%mod; } } } return c; } martix powmo(martix a,int n) { martix T; for(int i=0;i<2;i++) { T.mo[i][i]=1; } while(n) { if(n&1) T=mul(a,T); n>>=1; a=mul(a,a); } return T; } int main() { int t; cin>>t; q.mo[0][0]=5;q.mo[0][1]=12;q.mo[1][0]=2;q.mo[1][1]=5; while(t--) { int n; cin>>n; martix res=powmo(q,n); cout<<(2*res.mo[0][0]-1)%mod<<endl; } return 0; } K:So Easy! 大意:如图$0< a, m < 2^{15}, (a-1)^2< b < a^2, 0 < b, n < 2^{31}$ 套用D题步骤,可以得到$$ \\begin{bmatrix} x_n \\\\ y_n \\end{bmatrix}= \\begin{bmatrix} a & b \\\\ 1 & a \\end{bmatrix} \\times \\begin{bmatrix} x_{n-1} \\\\ y_{n-1} \\end{bmatrix}$$$$ \\because (a+\\sqrt{b})^n+(a-\\sqrt{b})^n=x_n+y_n\\sqrt{b}+x_n+y_n\\sqrt{b}=2x_n \\\\ \\because (a-\\sqrt{b})^n<1 \\\\ \\therefore \\lceil (a+\\sqrt{b})^n \\rceil = 2x_n$$ 最后套矩阵快速幂模板啦 View Codec++ #include <cstdio> #include <string.h> #include <cmath> #include <algorithm> #include <iostream> using namespace std; typedef long long ll; ll a,b,n,m; struct martix { ll mo[3][3]; martix() { memset(mo,0,sizeof(mo)); } }; martix mul(martix a,martix b) { martix c; for(int i=0;i<2;i++) { for(int j=0;j<2;j++) { for(int k=0;k<2;k++) { c.mo[i][j]=(c.mo[i][j]+a.mo[i][k]*b.mo[k][j])%m; } } } return c; } martix powmo(martix a,ll p) { martix T; for(int i=0;i<2;i++) { T.mo[i][i]=1; } while(p) { if(p&1) T=mul(T,a); p>>=1; a=mul(a,a); } return T; } int main() { while(cin>>a>>b>>n>>m) { martix q; q.mo[0][0]=a;q.mo[0][1]=b;q.mo[1][0]=1;q.mo[1][1]=a; martix res=powmo(q,n); cout<<2*res.mo[0][0]%m<<endl; } return 0; } E:Queuing 大意:用f,m,构成长度为$2^L$的字符串,如果含有fmf,fff的子串,则不为安全的,求安全的字符串个数$mod M$ 如果第$n$位是$f$,它前面是$f$时$(ff)$,再前一位必须是$m(mff)$,再前一位还必须是$m(mmff)$,所以有$f(n-4)$种;它前面是$m$时$(mf)$,再前一位必须是$m(mmf)$,再前就任意了,所以有$f(n-3)$种 第$n$位是$m$,它前面可以是任意的,所以有$f(n-1)$种 综上所述:$ ans=f(n-1)+f(n-3)+f(n-4) $ 化为矩阵式,即$$ \\begin{bmatrix} f(n) \\\\ f(n-1) \\\\ f(n-2) \\\\ f(n-3) \\end{bmatrix}= \\begin{bmatrix} 1&0&1&1 \\\\ 1&0&0&0 \\\\ 0&1&0&0 \\\\ 0&0&1&0 \\end{bmatrix} \\begin{bmatrix} f(n-1) \\\\ f(n-2) \\\\ f(n-3) \\\\ f(n-4) \\end{bmatrix}$$ 仿照D、K题套矩阵快速幂模板即可 View Codec++ #include <cstdio> #include <string.h> #include <iostream> #include <algorithm> #include <cmath> using namespace std; typedef long long ll; const int maxn = 1e6+5; int l,m,f[maxn]; struct martix { int mo[5][5]; martix() { memset(mo,0,sizeof(mo)); } }; martix mul(martix a,martix b) { martix c; for(int i=1;i<=4;i++) { for(int j=1;j<=4;j++) { for(int k=1;k<=4;k++) { c.mo[i][j]=(c.mo[i][j]+a.mo[i][k]*b.mo[k][j])%m; } } } return c; } martix powmo(martix a,ll n) { martix T; for(int i=1;i<=4;i++) { T.mo[i][i]=1; } while(n) { if(n&1) T=mul(T,a); n>>=1; a=mul(a,a); } return T; } int main() { f[0]=0;f[1]=2;f[2]=4;f[3]=6;f[4]=9; martix q; while(cin>>l>>m) { if(l<=4) { cout<<f[l]%m<<endl; continue; } q.mo[1][1]=1;q.mo[1][2]=0;q.mo[1][3]=1;q.mo[1][4]=1; q.mo[2][1]=1;q.mo[2][2]=0;q.mo[2][3]=0;q.mo[2][4]=0; q.mo[3][1]=0;q.mo[3][2]=1;q.mo[3][3]=0;q.mo[3][4]=0; q.mo[4][1]=0;q.mo[4][2]=0;q.mo[4][3]=1;q.mo[4][4]=0; q=powmo(q,l-4); ll ans=0; for(int i=1;i<=4;i++) { ans=(ans+q.mo[1][i]*f[4-i+1])%m; } cout<<ans<<endl; } return 0; }","categories":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"数据结构","slug":"数据结构","permalink":"http://WigginsLi.github.io/categories/数据结构/"}],"tags":[{"name":"矩阵快速幂","slug":"矩阵快速幂","permalink":"http://WigginsLi.github.io/tags/矩阵快速幂/"},{"name":"单调栈","slug":"单调栈","permalink":"http://WigginsLi.github.io/tags/单调栈/"},{"name":"单调队列","slug":"单调队列","permalink":"http://WigginsLi.github.io/tags/单调队列/"}],"keywords":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"数据结构","slug":"数据结构","permalink":"http://WigginsLi.github.io/categories/数据结构/"}]},{"title":"待补 < 基础数论 > 2019 GDUT Winter Training IV","slug":"sltrain","date":"2019-03-13T11:17:16.000Z","updated":"2019-03-15T05:30:28.350Z","comments":true,"path":"2019/03/13/sltrain/","link":"","permalink":"http://WigginsLi.github.io/2019/03/13/sltrain/","excerpt":"$\\implies$ 专题地址","text":"$\\implies$ 专题地址","categories":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"数论","slug":"数论","permalink":"http://WigginsLi.github.io/categories/数论/"}],"tags":[],"keywords":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"数论","slug":"数论","permalink":"http://WigginsLi.github.io/categories/数论/"}]},{"title":"待补 < 图论基础 > 2019 GDUT Winter Training IV","slug":"tltrain","date":"2019-03-13T11:16:43.000Z","updated":"2019-03-15T05:30:20.205Z","comments":true,"path":"2019/03/13/tltrain/","link":"","permalink":"http://WigginsLi.github.io/2019/03/13/tltrain/","excerpt":"$\\implies$ 专题地址","text":"$\\implies$ 专题地址","categories":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"图论","slug":"图论","permalink":"http://WigginsLi.github.io/categories/图论/"}],"tags":[],"keywords":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"图论","slug":"图论","permalink":"http://WigginsLi.github.io/categories/图论/"}]},{"title":"组合数公式","slug":"zhsgs","date":"2019-03-12T16:02:47.000Z","updated":"2019-03-15T05:30:09.790Z","comments":true,"path":"2019/03/13/zhsgs/","link":"","permalink":"http://WigginsLi.github.io/2019/03/13/zhsgs/","excerpt":"无奈数学太菜了,开一篇博文来记一下公式(:з」∠)证明的过程先留个坑吧,我太菜了不会证明不定期更新ing","text":"无奈数学太菜了,开一篇博文来记一下公式(:з」∠)证明的过程先留个坑吧,我太菜了不会证明不定期更新ing 基础公式 通项公式 $$ C_n^m = \\frac {n!}{m!(n-m)!} $$证明:略 $$ C_n^m = C_n^{n-m}$$证明:可由通项公式推出 $$ C_n^m = C_{n-1}^{m-1} + C_{n-1}^m $$证明: $$ \\sum_{i=0}^m C_{n+i}^i = C_{n+m+1}^m $$证明: $$ \\sum_{i=0}^n C_{n}^i = 2^n $$证明:推广:$$ \\implies \\sum_{i=0}^n C_{n}^i*x^i = (x+1)^n $$ 进阶公式: $$ C_n^m * C_m^r = C_n^r * C_{n-r}^{m-r} $$证明: 待补… $Lucas$定理: $$ C_n^m \\% p= C_{n/p}^{m/p}*C_{n\\%p}^{m\\%p} $$证明: 参考博客:(1)litble","categories":[{"name":"组合数学","slug":"组合数学","permalink":"http://WigginsLi.github.io/categories/组合数学/"}],"tags":[{"name":"组合数","slug":"组合数","permalink":"http://WigginsLi.github.io/tags/组合数/"}],"keywords":[{"name":"组合数学","slug":"组合数学","permalink":"http://WigginsLi.github.io/categories/组合数学/"}]},{"title":"< 组合数学初步 > 2019 GDUT Winter Training III","slug":"zhmath-training","date":"2019-03-07T00:36:57.000Z","updated":"2019-08-09T11:07:13.308Z","comments":true,"path":"2019/03/07/zhmath-training/","link":"","permalink":"http://WigginsLi.github.io/2019/03/07/zhmath-training/","excerpt":"$\\implies$专题地址数学也是$ACM$里面非常重要的一部分,这其中就包括组合数学感觉专题里面的题还是不够,没能打出感觉(:з」∠)继续钻研《组合数学》黑书ヾ(◍°∇°◍)ノ゙","text":"$\\implies$专题地址数学也是$ACM$里面非常重要的一部分,这其中就包括组合数学感觉专题里面的题还是不够,没能打出感觉(:з」∠)继续钻研《组合数学》黑书ヾ(◍°∇°◍)ノ゙ 初步知识储备 墙裂推荐$ACdreamers$大神的博客(不只是这一篇),数学太强啦,能学到很多%%%% 基本的组合数公式$C_n^m = \\frac {n!}{m!(n-m)!}$ 由上式延伸的$C_n^m = C_{n-1}^{m-1} + C_{n-1}^m \\implies$构造杨辉三角 容斥原理(概念) 【进阶应用资料PDF】 鸽笼原理 质因数分解 唯一分解定理(算术基本定理)$\\implies n = p_1^{a_1} \\times p_2^{a_2} \\times p_3^{a_3} \\times \\cdots \\times p_n^{a_n}$ 逆元(这个好像得归在数论,但属于本节的前提知识)$\\circ$拓展欧几里得求逆元$\\circ$费马小定理求逆元$\\circ$线性求逆元-$Lucas$定理(求$C_n^m mod P$) 题目讲解质因数分解+容斥E:Happy 2006 大意:与数$n$互质的数升序排列,输出第$k$个数($n \\in [1,1e6],k \\in [1,1e8]$) 我们可以根据 (数据范围) 互质数个数单增的性质想到,如果能求出 $[0,x]$ 中与 $n$ 互质的数的 $个数^{(1)}$ ,那么就可以二分这个$x$,比较 $check(x)$ 与 $k$ 的大小关系即可 (1):对 $n$ 分解质因数,然后利用容斥原理求出[0,x]范围内所有与n互质的数$$ans = \\frac {x}{p[1]} + \\frac {x}{p[2]} + \\cdots + \\frac {x}{p[n]} - \\frac {x}{p[1]p[2]} + \\cdots + (-1)^{n+1} \\times \\frac {x}{p[1]p[2] \\cdots p[n]}$$ View Code cpp #include <iostream> #include <algorithm> #include <cstdio> #include <cmath> #include <string.h> using namespace std; typedef long long ll; ll a[50],b[1010],cnt; int m,k; void div(ll num) { cnt=0; for(ll i=2;i*i<=num;i++) { if(num%i==0) { while(num%i==0) num/=i; a[++cnt]=i; } } if(num>1) a[++cnt]=num; } ll get_cnt(ll mid)//1--mid之间与n互质的数有多少个 { ll g=0,sum=mid,t; b[++g]=1; for(ll i=1; i<=cnt; i++) { t=g; for(ll j=1; j<=g; j++) { b[++t]=b[j]*a[i]*-1; sum+=mid/b[t]; } g=t; } return sum; } int main() { while(cin>>m>>k) { div(m); ll l=0,r=1e18,mid; while(l<r-1) { mid=(l+r)/2; ll u=get_cnt(mid); if(u>=k) { r=mid; }else { l=mid+1; } } if(get_cnt(l)==k) cout<<l<<endl; else cout<<r<<endl; } return 0; } F:Sky Code 大意:$n$个数字中找出4个数字的$gcd$为1(ps:不是两两互质),求能找出多少组这样的4个数字 直接求值比较困难,我们可以反过来想,求有$sum$组数字的$gcd$不为$1^{(1)}$,再求$C_n^4 - sum$ (1):我们对于每个数字进行质因数分解,并统计每个质因子$p[i]$(不为1)的个数$num[p[i]]$,那么对于每个质因子,能组成的组数为$C_{num[p[i]]}^4$之后对每个质因子进行容斥即可 View Code cpp #include <iostream> #include <cstdio> #include <cstring> using namespace std; typedef long long ll; const int maxn = 1e4+10; int n,m; int cnt[maxn];//cnt[i]记录所给数据中,含有因数i的个数有几个 int num[maxn];//num[i]记录所给数据中,因数i中含有几个不同素因子 ll c[maxn];//c[i]表示组合数C(i,4) int prime[maxn]; void init(){ for(ll i = 4; i < maxn; i++){ c[i] = i * (i - 1) * (i - 2) * (i - 3) / 24; } } void divide(int n){ int tot = 0; for(int i = 2; i * i <= n; i++){ if(n % i == 0){ prime[tot++] = i; while(n % i == 0) n /= i; } } if(n > 1) prime[tot++] = n;//分解质因子 for(int i = 1; i < (1<<tot); i++){//枚举质因子组合情况得到所有可能因子 int tmp = 1; int sum = 0; for(int j = 0; j < tot; j++){ if(i & (1 << j)){ tmp * = prime[j]; sum++; } } cnt[tmp]++;//说明数n包含tmp这种因子,所以个数+1 num[tmp] = sum;//tmp这种因子内含有sum个不同质因子 } } int main(){ init(); memset(num,0,sizeof(num)); while(scanf("%d",&n) != EOF){ memset(cnt,0,sizeof(cnt)); for(int i = 0; i < n; i++){ scanf("%d",&m); divide(m);//质因子分解统计相关数据 } for(int i=2;i<=n;i++) {cout<<cnt[i]<<" ";}cout<<endl; for(int i=2;i<=n;i++) {cout<<num[i]<<" ";}cout<<endl; ll ans = 0; for(int i = 2; i < maxn; i++){ if(cnt[i] >= 4){ if(num[i] & 1) ans += c[cnt[i]];//因子i中含有的质因子数为奇数加 else ans -= c[cnt[i]];//否则减 } } printf("%lld\\n",c[n]-ans); } return 0; } G:Co-prime 大意:给出$n$,求$[A,B]$范围内与$n$互质的数的个数 E题的退化版,只需要求 $x \\in (0,A]$ 及 $x \\in (0,B]$ 内与 $n$ 互质的数量再相减即可$\\implies$质因数分解、容斥原理 View Code cpp #include <iostream> #include <algorithm> #include <cstdio> #include <cmath> #include <string.h> using namespace std; typedef long long ll; ll a[50],b[1010],cnt,n,m,p; int T; void div(ll num) { cnt=0; for(ll i=2;i*i<=num;i++) { if(num%i==0) { while(num%i==0) num/=i; a[++cnt]=i; } } if(num>1) a[++cnt]=num; } ll get_cnt(ll mid)//1--mid之间与n互质的数有多少个 { ll g=0,sum=mid,t; b[++g]=1; for(ll i=1; i<=cnt; i++) { t=g; for(ll j=1; j<=g; j++) { b[++t]=b[j]*a[i]*-1; //cout<<i<<" "<<j<<" "<<b[t]<<endl; sum+=mid/b[t]; } g=t; } return sum; } int main() { cin>>T; int t=0; while(++t<=T) { cin>>n>>m>>p; div(p); cout<<"Case #"<<t<<": "; cout<<get_cnt(m)-get_cnt(n-1)<<endl; } return 0; } 组合数(杨辉三角+lucas定理)H:chess 大意:在$n*m$的棋盘中摆最多数量的棋子,使他们互不在同一行同一列,且两棋子中行号小的,列号一定大 假设$n>m$,放置最多数量的棋子即每一列都放置一个,且题目限制了棋子摆放顺序,那么就可以$n$行中选出$m$个来按顺序摆放棋子,结果即为$C_n^m$ 数据范围比较小,可以用杨辉三角预处理出组合数 View Code cpp #include <iostream> #include <algorithm> #include <cstdio> #include <string.h> #include <cmath> using namespace std; typedef long long ll; const int mod=1e9+7; const int maxn=1005; ll c[maxn][maxn]; int T,n,m; void init() { c[0][0]=1; c[1][0]=1; c[1][1]=1; for(int i=2;i<=1001;i++) { c[i][0]=1; for(int j=1;j<=i;j++) { c[i][j]=(c[i-1][j-1]%mod+c[i-1][j]%mod)%mod; } } } int main() { init(); cin>>T; while(T--) { cin>>n>>m; if(m>n) swap(m,n); cout<<c[n][m]<<endl; } return 0; } I:Saving Beans 大意:在$n$棵树(每棵树可以不放,最多可以放无数个)上最多放$m$个果子的方案数 考虑只放$m$个果子,则根据隔板法求得方案数为$C_{n+m-1}^{n-1}$$ps$:相当于有$n+m$个果子,有$n+m-1$个空隙,插$n-1$个板,分成$n$个部分,然后每个部分的果子数减一即为每棵树上的果子数 同理,当有$x \\in [0,m)$个果子时,方案数为$C_{n+x-1}^{n-1}$,得:$$ans = \\sum_{i=1}^m C_{n+i-1}^{n-1}$$$$= \\sum_{i=1}^m C_{n+i-1}^{i}$$$$= C_{n+m}^m$$此处链接组合数公式(施工ing) 但是我们发现n,m太大了,预处理杨辉三角会T,这里我们引入Lucas定理(证明见上链接):$$C_n^m \\% p= C_{n/p}^{m/p}* C_{n\\%p}^{m\\%p}$$ 过程中需要求$\\frac{1}{m!}\\%p$,即求逆元,可用导言三种方法求解,在此不多赘言 (公式应用具体见代码)至此,问题解决 View Code cpp #include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #include <string.h> using namespace std; #define mod 1000000007 typedef long long ll; const int maxn = 1e5+5; int T; ll n,m,p,d[maxn]; ll ksm(ll a,ll b) { if(b==0) return 1; ll ans=ksm(a,b/2)%p; ans=ans*ans%p; if(b&1) ans=ans*a%p; return ans; } void init() { d[0]=1; for(int i=1;i<=p;i++) { d[i]=d[i-1]* i%p; } } ll C(ll a,ll b) { if(b>a) return 0; return d[a]* ksm(d[b],p-2)%p*ksm(d[a-b],p-2)%p; } ll lucas(ll a,ll b) { if(b==0) return 1; return C(a%p,b%p)* lucas(a/p,b/p)%p; } int main() { int T; scanf("%d",&T); while(T--) { scanf("%lld %lld %lld",&n,&m,&p); init(); printf("%lld\\n",lucas(n+m,m)); } return 0; } J:瞬间移动 大意:一无限大矩形中,初始在(1,1),每次可以选择格子$(nx,ny){nx>x,ny>y}$,并瞬移过去,求到第n行第m列的格子有几种方案,答案对1000000007取模 打表一波,可以发现矩形是一个向左上角斜45°的杨辉三角。 再找一波规律,发现$ans[n][m]\\%p = C_{n+m-4}^{m-2}\\%p$ 最后套一波费马小定理求逆元的模板即可(本质是一道模板题/逃) View Code cpp #include <iostream> #include <algorithm> #include <cstdio> #include <string.h> using namespace std; #define mod 1000000007 typedef long long ll; int n,m; ll qzc[200050]; void init() { qzc[0]=1; for(int i=1;i<=200000;i++) { qzc[i]=qzc[i-1]* i%mod; } } ll ksm(ll a,ll b) { if(b==0) return 1; ll u = ksm(a,b/2)%mod; u=u*u%mod; if(b&1) u=u*a%mod; return u; } int main() { init(); while(cin>>n>>m) { ll a=qzc[n+m-4]; ll b=qzc[m-2]; ll c=qzc[n-2]; ll ans=a*ksm(b,mod-2)%mod*ksm(c,mod-2)%mod; cout<<ans<<endl; } return 0; } M:How Many Sets II 大意:从$1,2,\\cdots,n$中选中m个球,要求两两不相邻,求有方案数 隔板法应用,如果我们先不编号,随便取出$m$个球,剩下$n-m$个球在$n-m+1$个空隙中插入m个球,之后再编号,那么我们插入的球所在的编号即为本次挑选的球,即$C_{n-m+1}^m$ 套用$Lucas$模板即可(注意到$p \\in [1,1e9]$,直接预处理组合数可能会超时,如$I$题代码) N:DP? 大意:杨辉三角上,求$[0][0]$到$[n][k]$路径上的最小和(可向下及左下方向)$mod$$p$$1e5组数据,n,k \\in [0,1e9],p \\in (0,1e4),且p为质数$ 画出杨辉三角,发现$[n][k]$到$[0][0]$的路径为斜向左上(或上)直到边界再沿着1的路径走到顶点(如图表示$[4][2]$及$[4][3]$的最短路径和) 所以我们可以得到公式(具体推导公式见上组合数公式链接):$$ ans[n][k]= \\begin{cases} C_{n+1}^m+n-m, & \\text{if$k<=\\frac{n}{2}$} ,\\\\ C_{n+1}^{n-m}+m, & \\text{if$k>\\frac{n}{2}$} \\end{cases}$$ 之后就是开心套Lucas模板啦(才怪)$\\implies TLE$~本题有1e5组数据,每一组预处理一下需要1e4,1e9>1e8~(ps:因为log的时间影响较小,本题计算复杂度不考虑) 注意到$p$为质数,打一下质数表发现,$(0,1e4)$的质数只有1000+个那我们直接预处理出每个$mod$$p$对应的组合数表$\\implies 1e3*1e4+1e5<1e8 \\implies AC$ View Code cpp #include <iostream> #include <cstdio> #include <string.h> #include <cmath> #include <algorithm> using namespace std; #define mod 1000000007 typedef long long ll; const int maxn = 1e4+5; int T; ll n,m,p,d[1500][maxn]; int flag[maxn],prime[maxn],pnum=0,has[maxn]; ll ksm(ll a,ll b) { if(b==0) return 1; ll ans=ksm(a,b/2)%p; ans=ans*ans%p; if(b&1) ans=ans*a%p; return ans; } void InitPrime() { for(int i=0;i<=1e4;i++) flag[i]=1; for(int i=2;i<=1e4;i++) { if(flag[i]==1) has[i]=pnum,prime[pnum++]=i; for(int j=0;j<pnum&&prime[j]*i<=1e4;j++) { flag[prime[j]* i]=0; if(i%prime[j]==0) break; } } //cout<<pnum<<endl;//1229 } void init() { InitPrime(); for(int k=0;k<pnum;k++) { d[k][0]=1; for(int i=1;i<=prime[k];i++) { d[k][i]=d[k][i-1]* i%prime[k]; } } } ll C(ll a,ll b) { //cout<<has[p]<<endl; if(b>a) return 0; return d[has[p]][a]* ksm(d[has[p]][b],p-2)%p*ksm(d[has[p]][a-b],p-2)%p; } ll lucas(ll a,ll b) { if(b==0) return 1; return C(a%p,b%p)* lucas(a/p,b/p)%p; } int main() { int t=1; init(); while(~scanf("%lld%lld%lld",&n,&m,&p)) { if(m<=n/2) printf("Case #%d: %lld\\n",t++,(n-m+lucas(n+1,m))%p); else printf("Case #%d: %lld\\n",t++,(m+lucas(n+1,n-m))%p); } return 0; }","categories":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"组合数学","slug":"组合数学","permalink":"http://WigginsLi.github.io/categories/组合数学/"}],"tags":[{"name":"费马小定理","slug":"费马小定理","permalink":"http://WigginsLi.github.io/tags/费马小定理/"},{"name":"组合数","slug":"组合数","permalink":"http://WigginsLi.github.io/tags/组合数/"},{"name":"GCD","slug":"GCD","permalink":"http://WigginsLi.github.io/tags/GCD/"},{"name":"杨辉三角","slug":"杨辉三角","permalink":"http://WigginsLi.github.io/tags/杨辉三角/"},{"name":"逆元","slug":"逆元","permalink":"http://WigginsLi.github.io/tags/逆元/"},{"name":"容斥原理","slug":"容斥原理","permalink":"http://WigginsLi.github.io/tags/容斥原理/"},{"name":"lucas定理","slug":"lucas定理","permalink":"http://WigginsLi.github.io/tags/lucas定理/"}],"keywords":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"组合数学","slug":"组合数学","permalink":"http://WigginsLi.github.io/categories/组合数学/"}]},{"title":"< 数位dp > 2019 GDUT Winter Training III","slug":"swDP-trainning","date":"2019-03-05T00:09:28.000Z","updated":"2019-03-15T05:30:37.390Z","comments":true,"path":"2019/03/05/swDP-trainning/","link":"","permalink":"http://WigginsLi.github.io/2019/03/05/swDP-trainning/","excerpt":"$ \\implies $专题地址数位$ dp $属于套模板类型的题,且本身非常冷门,赛场上碰到的可能性很低,但我们还是得学啊。$ \\implies $不错的博客讲解刷上几道题基本就理解了(。・ω・。)$ ps $ :本文首次采用$ mathjax $","text":"$ \\implies $专题地址数位$ dp $属于套模板类型的题,且本身非常冷门,赛场上碰到的可能性很低,但我们还是得学啊。$ \\implies $不错的博客讲解刷上几道题基本就理解了(。・ω・。)$ ps $ :本文首次采用$ mathjax $ 数位$ dp $ 数位$ dp $用于求解在某一段范围(极大)内满足条件的数的个数,且条件与数的每个位数有关 我们从题目入手理解: K:Bomb 大意 : 求 $ x \\in [1,n] $ 中满足 $ x $ 的位数中存在 49的数字个数 定义一个状态 $ pre $ 表示前一位是否为4,$ pos $表示当前位,$num[pos]==9\\&\\&pre==1 $ 时,该数满足条件。 用于熟悉模板的水题。 放两个模板 View Code可判断前导零c++ #include <iostream> #include <algorithm> #include <string.h> #include <cstdio> #include <cmath> using namespace std; typedef long long ll; const int maxn = 1e5; int T; ll l,r,len,a[maxn],dp[20][10]; ll dfs(int pos,int pre,int lead,int limit) { if(pos>len) return 1; if(dp[pos][pre]!=-1&&!limit&&!lead) return dp[pos][pre]; ll ret=0; int res=limit?a[len-pos+1]:9;//res当前为能取到的最大值 for(int i=0;i<=res;i++) { if(pre&&i==9) continue; //有前导零并且当前位也是前导零 if((!i)&&lead) ret+=dfs(pos+1,0,1,i==res&&limit); //有前导零但当前位不是前导零,当前位是最高位 else if(i&&lead) ret+=dfs(pos+1,i==4,0,i==res&&limit); else ret+=dfs(pos+1,i==4,0,i==res&&limit); } if(!limit&&!lead) dp[pos][pre]=ret; return ret; } ll part(ll x) { len=0; while(x) a[++len]=x%10,x/=10; memset(dp,-1,sizeof(dp)); return dfs(1,0,1,1); } int main() { scanf("%d",&T); while(T--) { scanf("%lld",&r); printf("%lld\\n",r-part(r)+1);//[l,r](l!=0) } return 0; } ViewCode省略前导零判断c++ #include <iostream> #include <algorithm> #include <string.h> #include <cstdio> #include <cmath> using namespace std; typedef long long ll; const int maxn = 1e5; int T; ll l,r,len,a[maxn],dp[20][10]; ll dfs(int pos,int pre,int limit) { if(pos>len) return 1; if(dp[pos][pre]!=-1&&!limit) return dp[pos][pre]; ll ret=0; int res=limit?a[len-pos+1]:9;//res当前为能取到的最大值 for(int i=0;i<=res;i++) { if(pre&&i==9) continue; ret+=dfs(pos+1,i==4,i==res&&limit); } if(!limit) dp[pos][pre]=ret; return ret; } ll part(ll x) { len=0; while(x) a[++len]=x%10,x/=10; memset(dp,-1,sizeof(dp)); return dfs(1,0,1); } int main() { scanf("%d",&T); while(T--) { scanf("%lld",&r); printf("%lld\\n",r-part(r)+1);//[l,r](l!=0) } return 0; } L:不要62 大意 : $ x \\in [n,m] $ 中多少数不含62或4 同上题,添加一个条件判断$ num[pos] == 4 $即可 又是一道练模板的水题 ViewCodec++ #include <iostream> #include <algorithm> #include <string.h> #include <cstdio> #include <cmath> using namespace std; typedef long long ll; ll l,r,len,a[20],dp[20][10]; ll dfs(int pos,int pre,int limit) { if(pos>len) return 1; if(dp[pos][pre]!=-1&&!limit) return dp[pos][pre]; ll ret=0; int res=limit?a[len-pos+1]:9;//res当前为能取到的最大值 for(int i=0;i<=res;i++) { if(i==4||(pre&&i==2)) continue; ret+=dfs(pos+1,i==6,i==res&&limit); } if(!limit) dp[pos][pre]=ret; return ret; } ll part(ll x) { len=0; while(x) a[++len]=x%10,x/=10; memset(dp,-1,sizeof(dp)); return dfs(1,0,1); } int main() { while(scanf("%lld %lld",&l,&r)&&r) { if(l) printf("%lld\\n",part(r)-part(l-1));//[l,r](l!=0) else printf("%lld\\n",part(r)-part(l));//从0开始要特判 } return 0; } 下面是稍微高级一点的 A:Round Numbers 大意 : 若N的二进制中的0的个数 $ \\geq $ 1的个数$ \\implies $N为Round Number 定义两个状态$ zero $, $ one $记录0,1的个数,当遍历完一个数的所有位时判断即可。 View Codec++ #include <iostream> #include <algorithm> #include <string.h> #include <cstdio> #include <cmath> using namespace std; typedef long long ll; const int maxn = 64; ll l,r,len,a[maxn],dp[maxn][maxn][maxn]; ll dfs(int pos,int pre,int st,int lead,int limit) { if(pos>len) return pre>=st; if(dp[pos][pre][st]!=-1&&!limit&&!lead) return dp[pos][pre][st]; ll ret=0; int res=limit?a[len-pos+1]:1;//res当前为能取到的最大值 for(int i=0;i<=res;i++) { //有前导零并且当前位也是前导零 if((!i)&&lead) ret+=dfs(pos+1,0,0,1,i==res&&limit); //有前导零但当前位不是前导零,当前位是最高位 else if(i&&lead) ret+=dfs(pos+1,i==0,i==1,0,i==res&&limit); else { ret+=dfs(pos+1,pre+(i==0),st+(i==1),0,i==res&&limit); } } if(!limit&&!lead) dp[pos][pre][st]=ret; return ret; } ll part(ll x) { len=0; while(x) a[++len]=x%2,x/=2; memset(dp,-1,sizeof(dp)); return dfs(1,0,0,1,1); } int main() { scanf("%lld %lld",&l,&r); printf("%lld\\n",(part(r)-part(l-1))); return 0; } B:Balanced Number 大意:对于有$ m $ 位的数 $ N $ ,存在位 $ b_i $ ,使 $$ b_{i-1} \\times 1 +b_{i-2} \\times 2+ \\cdots +b_1 \\times a = b_{i+1}\\times 1+b_{i+2}\\times 2+ \\cdots + b_{m}\\times b $$ 其中a+b+1=m 定义一个 $ sum $ 状态,枚举中轴心 $ b_i $ 再进行 $ DFS $ 需要注意的是,对于0这个数学,每个轴心都成立导致多算$len-1$次 View Codec++ #include <iostream> #include <algorithm> #include <string.h> #include <cstdio> #include <cmath> using namespace std; typedef long long ll; const int maxn = 1e5; int T; ll l,r,len,a[20],dp[20][20][5000]; ll dfs(int pos,int mid,int sum,int lead,int limit) { if(pos<1) return sum?0:1; if(sum<0) return 0; if(dp[pos][mid][sum]!=-1&&!limit&&!lead) return dp[pos][mid][sum]; ll ret=0; int res=limit?a[pos]:9;//res当前为能取到的最大值 for(int i=0;i<=res;i++) { //有前导零并且当前位也是前导零 if((!i)&&lead) ret+=dfs(pos-1,mid,sum,1,i==res&&limit); //有前导零但当前位不是前导零,当前位是最高位 else if(i&&lead) ret+=dfs(pos-1,mid,sum+(pos-mid)*i,0,i==res&&limit); else ret+=dfs(pos-1,mid,sum+(pos-mid)*i,0,i==res&&limit); } if(!limit&&!lead) dp[pos][mid][sum]=ret; return ret; } ll part(ll x) { len=0; while(x) a[++len]=x%10,x/=10; ll ans=0; for(int i=1;i<=len;i++) { ans+=dfs(len,i,0,1,1); } return ans-(len-1); } int main() { scanf("%d",&T); memset(dp,-1,sizeof(dp)); while(T--) { scanf("%lld%lld",&l,&r); printf("%lld\\n",part(r)-part(l-1));//[l,r](l!=0) } return 0; } C:F(x) 大意:对于$ A(A_nA_{n-1}A_{n-2} \\ldots A_1) $ .有$$ F(A)=A_1\\times 1+A_2\\times 2+\\cdots+A_{n-1}\\times 2^n$$求满足$ F(x) \\leq F(A),x\\subseteq[0,B] $的$x$的数量 与上题差不多的思路 这里先算出$F(A)$,然后再减的写法比较好写,但是要注意小于0时及时跳出,否则数组越界错误 View Codec++ #include <iostream> #include <algorithm> #include <string.h> #include <cstdio> #include <cmath> using namespace std; typedef long long ll; int T; ll l,r,len,a[15],dp[15][50005]; ll F(int x) { ll sum=0,e=1; while(x) { sum+=x%10*e; x/=10; e*=2; } return sum; } ll dfs(int pos,int sum,int limit) { if(pos<0) return sum>=0; if(sum<0) return 0; if(dp[pos][sum]!=-1&&!limit) return dp[pos][sum]; ll ret=0; int res=limit?a[pos]:9;//res当前为能取到的最大值 for(int i=0;i<=res;i++) { ret+=dfs(pos-1,sum-i*(1<<pos),i==res&&limit); } if(!limit) dp[pos][sum]=ret; return ret; } ll part(ll x) { len=0; while(x) a[len++]=x%10,x/=10; return dfs(len-1,F(l),1); } int main() { scanf("%d",&T); int t=0; memset(dp,-1,sizeof(dp)); while(++t<=T) { scanf("%lld %lld",&l,&r); printf("Case #%d: %lld\\n",t,part(r)); } return 0; } 比较难的进阶题目 D:Beautiful Numbers 大意:对于一个数$N$,若其可被任意组成它的位整除,即可称为$Beautiful Number$ 容易想到,$N$可被任意位整除 $\\Leftrightarrow N\\%lcm(b_1,b_2, \\ldots ,b_n)=0$且 $lcm(1,2,3,\\ldots,9)=2520$$\\,\\,\\,\\,\\, \\Longrightarrow 需要 \\underbrace{[20]}_{位数} \\times \\underbrace{[N\\%2520]}_{N\\%lcm=N\\%2520\\%lcm}\\times \\underbrace{[2520]}_{lcm状态数}$的状态(20 * 2520* 2520 $\\approx$ 1e8) * 10 = 1e9 $\\rightarrow$ TLE$\\,\\,\\,\\,\\,\\Longrightarrow$我们发现,1-9随机组合的$lcm$最多只有48种,对2520离散成48,那么状态缩减至20* 2520* 50*10 $\\approx$ 2e7 ViewCodec++ #include <iostream> #include <algorithm> #include <string.h> #include <cstdio> #include <cmath> using namespace std; typedef long long ll; int T,has[2600],fhas[50]; ll l,r,len,a[20],dp[20][2600][50]; int mygcd(int a,int b) { if(b==0) return a; return mygcd(b,a%b); } int mylcm(int a,int b) { return (a*b)/mygcd(a,b); } ll dfs(int pos,int sum,int nlcm,int lead,int limit) { //cout<<sum<<" "<<fhas[nlcm]<<endl; if(pos<1) return sum%fhas[nlcm]==0; if(dp[pos][sum][nlcm]!=-1&&!limit) return dp[pos][sum][nlcm]; ll ret=0; int res=limit?a[pos]:9;//res当前为能取到的最大值 for(int i=0;i<=res;i++) { int u=fhas[nlcm]; if(i) u=mylcm(fhas[nlcm],i); //cout<<u<<" "<<fhas[nlcm]<<" "<<nlcm<<" "<<i<<endl; int p=(sum*10+i)%2520; ret+=dfs(pos-1,p,has[u],0,i==res&&limit); } if(!limit) dp[pos][sum][nlcm]=ret; return ret; } ll part(ll x) { len=0; while(x) a[++len]=x%10,x/=10; return dfs(len,0,1,1,1); } int init() { int tot=1; for(int i=1;i<=2520;i++) { if(2520%i==0) { has[i]=tot; fhas[tot]=i; //cout<<i<<endl; tot++; } } } int main() { init(); scanf("%d",&T); memset(dp,-1,sizeof(dp)); while(T--) { scanf("%I64d %I64d",&l,&r); printf("%I64d\\n",part(r)-part(l-1));//[l,r](l!=0) } return 0; }","categories":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"动态规划","slug":"动态规划","permalink":"http://WigginsLi.github.io/categories/动态规划/"}],"tags":[{"name":"DP","slug":"DP","permalink":"http://WigginsLi.github.io/tags/DP/"},{"name":"数位DP","slug":"数位DP","permalink":"http://WigginsLi.github.io/tags/数位DP/"}],"keywords":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"动态规划","slug":"动态规划","permalink":"http://WigginsLi.github.io/categories/动态规划/"}]},{"title":"待补 < 概率DP > 2019 GDUT Winter Training II","slug":"gailv-dp-training","date":"2019-03-01T23:57:42.000Z","updated":"2019-03-15T05:29:46.150Z","comments":true,"path":"2019/03/02/gailv-dp-training/","link":"","permalink":"http://WigginsLi.github.io/2019/03/02/gailv-dp-training/","excerpt":"专题二vj链接 或许是知识储备不足,即便把概率DP的专题做了一遍,也不能很好地参透方程的意义。 特地把概率DP写成独立的一份。 这也意味着这份训练报告可能会挖下很多坑,希望铁牌选手WiLe之后能回来补呢。(๑*◡*๑)","text":"专题二vj链接 或许是知识储备不足,即便把概率DP的专题做了一遍,也不能很好地参透方程的意义。 特地把概率DP写成独立的一份。 这也意味着这份训练报告可能会挖下很多坑,希望铁牌选手WiLe之后能回来补呢。(๑*◡*๑) 概率DP虽然名字叫概率,但一般来说求得是期望值,有句话经常看到:"概率是顺着求的, 期望是逆着求的",不能理解(tcl orz)。但确实正确。 J:Dice (III) 大意:求将一个筛子扔出n个面要扔次数的期望值。 分析:gugugu View Codec++ #include <iostream> #include <cstdio> #include <algorithm> #include <string.h> using namespace std; int T,n; int main() { scanf("%d",&T); int t=1; while(t<=T) { scanf("%d",&n); double ans=0; for(int i=1;i<=n;i++) { ans+=n/(double)i; } printf("Case %d: %lf\\n",t++,ans); } return 0; } K:Discovering Gold 大意:从1到n的线性道路,起点在1st,每次掷骰子前进,到nth结束,走到每个格子可以得到各自的分数,求走到终点的期望分数。 分析:gugugu View Codec++ #include <cstdio> #include <iostream> #include <algorithm> #include <string.h> using namespace std; const int N=105; int a[N],n,T; double f[N]; int main() { scanf("%d",&T); int t=0; while(++t<=T) { scanf("%d",&n); for(int i=1;i<=n;i++) {scanf("%lf",&f[i]);} for(int i=n-1;i>=1;i--) { double x=min(6,n-i); for(int j=i+1;j<=i+x;j++) { f[i]+=f[j]/x; } } printf("Case %d: %lf\\n",t,f[1]); } return 0; } L:Flipping Coins 大意:给定n个硬币,初始全部反面朝上,可以且必须抛k次硬币(任选),求期望得到的金额(得到该硬币当且仅当正面朝上) 分析:gugugu View Codec++ #include <iostream> #include <algorithm> #include <cstdio> #include <string.h> using namespace std; const int maxn=450; double dp[maxn][maxn]; int main() { int n,k; cin>>n>>k; memset(dp,0,sizeof(dp)); dp[0][0]=1; for(int i=0; i<k; i++) { for(int j=0; j<n; j++) { dp[i+1][j]+=dp[i][j]*0.5; dp[i+1][j+1]+=dp[i][j]*0.5; } dp[i+1][n]+=dp[i][n]*0.5; dp[i+1][n-1]+=dp[i][n]*0.5; } double ans=0; for(int i=1; i<=n; i++) ans+=(dp[k][i]*i); printf("%.10f\\n",ans); }","categories":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"动态规划","slug":"动态规划","permalink":"http://WigginsLi.github.io/categories/动态规划/"}],"tags":[{"name":"概率DP","slug":"概率DP","permalink":"http://WigginsLi.github.io/tags/概率DP/"},{"name":"DP","slug":"DP","permalink":"http://WigginsLi.github.io/tags/DP/"}],"keywords":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"动态规划","slug":"动态规划","permalink":"http://WigginsLi.github.io/categories/动态规划/"}]},{"title":"记pytroch安装及入门","slug":"pytroch-install","date":"2019-03-01T01:17:59.000Z","updated":"2019-05-27T14:21:01.522Z","comments":true,"path":"2019/03/01/pytroch-install/","link":"","permalink":"http://WigginsLi.github.io/2019/03/01/pytroch-install/","excerpt":"昨天上了周老师的深度学习课程的选修,介绍到pytorch,之前只了解过TensorFlow,马上install来捣鼓捣鼓。本文小记一下安装过程和坑点(:з」∠) 3-3update:没想到因为一开始没有添加源导致的下载失败,中间一部分文件缺失了,创建虚拟环境的时候失败,谷歌了才发现这个问题(:з」∠) ,无奈重装。之后一切正常。","text":"昨天上了周老师的深度学习课程的选修,介绍到pytorch,之前只了解过TensorFlow,马上install来捣鼓捣鼓。本文小记一下安装过程和坑点(:з」∠) 3-3update:没想到因为一开始没有添加源导致的下载失败,中间一部分文件缺失了,创建虚拟环境的时候失败,谷歌了才发现这个问题(:з」∠) ,无奈重装。之后一切正常。 安装anaconda 官网似乎需要梯子,速度很慢。这里还是推荐清华镜像拉到最下面选择最新的适合自己系统的版本就好啦,这里不多赘述 添加镜像源 如果没有梯子的话,conda install极慢且不稳定,参照这里我们在cmd里面输入以下指令 conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/ conda config --set show_channel_urls yes ps:成功导入是没有提示的,如果重复导入会有提示 查看CUDA版本 打开控制面板,在右上角选择小图标,找到NVIDIA控制面板 帮助->系统信息 组件,得到版本,如下图,我是9.1 安装pytorch及其组件 打开官网 选择自己的系统、Python版本等等 这是根据我的情况选择的版本 将下面的代码输入cmd(注意:因为我们需要用到清华源,所以去掉后面的-c pytorch)即,我输入 conda install pytorch torchvision cudatoolkit=9.0 即可 可以看到这里已经使用了清华源,(别在意我需要下载的东西可能和你们的有差别,我这里是重试了很多次之后部分已经下好了的结果(:з」∠) ) 附上一开始没有使用清华源的惨剧 安装完成后的测试,all is ready!","categories":[{"name":"Experience","slug":"Experience","permalink":"http://WigginsLi.github.io/categories/Experience/"},{"name":"机器学习","slug":"机器学习","permalink":"http://WigginsLi.github.io/categories/机器学习/"}],"tags":[{"name":"pytroch","slug":"pytroch","permalink":"http://WigginsLi.github.io/tags/pytroch/"},{"name":"深度学习","slug":"深度学习","permalink":"http://WigginsLi.github.io/tags/深度学习/"}],"keywords":[{"name":"Experience","slug":"Experience","permalink":"http://WigginsLi.github.io/categories/Experience/"},{"name":"机器学习","slug":"机器学习","permalink":"http://WigginsLi.github.io/categories/机器学习/"}]},{"title":"待补 < 简单DP > 2019 GDUT Winter Training II","slug":"DP-training","date":"2019-02-27T18:59:34.000Z","updated":"2019-03-15T05:31:36.878Z","comments":true,"path":"2019/02/28/DP-training/","link":"","permalink":"http://WigginsLi.github.io/2019/02/28/DP-training/","excerpt":"专题地址现在还在简单DP的浅海扑腾扑腾dalao们已经可以胡乱写出正确转移方程了(:з」∠)","text":"专题地址现在还在简单DP的浅海扑腾扑腾dalao们已经可以胡乱写出正确转移方程了(:з」∠) 背包 背包问题具体的讲解真的讲不来,还是参考权威教程–背包九讲吧(:з」∠) 01背包01背包==>0/1==>选/不选,考虑的问题就是如何选取物品时背包价值最大。 思路看过资料之后可能会一头雾水,看着代码模拟一下dp过程能加深理解 ヾ(◍°∇°◍)ノ゙ A:Bone Collector 大意:给定一个有V容量的背包,给出N个物体的体积和价值,求能装出来的最大价值 分析:01背包模板题,注意学习从二维数组到一维数组的空间优化 代码如下:二维数组版本: View Codec++ #include <iostream> #include <algorithm> #include <cstdio> #include <string.h> using namespace std; const int maxn = 1e3+5; int f[maxn][maxn],T,n,v,c[maxn],w[maxn]; int main() { scanf("%d",&T); while(T--) { scanf("%d %d",&n,&v); for(int i=1;i<=n;i++) scanf("%d",&c[i]); for(int i=1;i<=n;i++) scanf("%d",&w[i]); memset(f,0,sizeof(f)); for(int i=1;i<=n;i++) { for(int j=0;j<=v;j++) { if(j<w[i]) f[i][j]=f[i-1][j]; else f[i][j]=max(f[i-1][j-w[i]]+c[i],f[i-1][j]); } } cout<<f[n][v]<<endl; } return 0; } 一维数组优化版本: View Codec++ #include <iostream> #include <algorithm> #include <cstdio> #include <string.h> using namespace std; const int maxn = 1e3+5; int f[maxn],T,n,v,c[maxn],w[maxn]; int main() { scanf("%d",&T); while(T--) { scanf("%d %d",&n,&v); for(int i=1;i<=n;i++) scanf("%d",&c[i]); for(int i=1;i<=n;i++) scanf("%d",&w[i]); memset(f,0,sizeof(f)); for(int i=1;i<=n;i++) { for(int j=v;j>=w[i];j--) { f[j]=max(f[j-w[i]]+c[i],f[j]); } } cout<<f[v]<<endl; } return 0; } B:CD 大意:给定M个CD,有各自的时长,求给定时限内能达到的最大CD播放时长需要输出播放的每个CD. 分析:还是01背包裸题,只是价值和体积相同.难点是输出dp路径(1*) (1*):当我们选取当前CD,记[i][j]=1,最后得出最大价值 时,往前减价值即可得出路径(表达不是很清楚,具体看代码) View Codec++ #include <cstdio> #include <cmath> #include <string.h> #include <iostream> #include <vector> #include <algorithm> using namespace std; const int N=10000+5; int f[25][N],n,v,a[N],g[25][N]; int main() { while(~scanf("%d %d",&v,&n)) { for(int i=1;i<=n;i++) scanf("%d",&a[i]); memset(f,0,sizeof(f)); memset(g,0,sizeof(g)); for(int i=1;i<=n;i++) { for(int j=v;j>=0;j--) { if(j<a[i]) f[i][j]=f[i-1][j]; else{ if(f[i-1][j]<=f[i-1][j-a[i]]+a[i]) { f[i][j]=f[i-1][j-a[i]]+a[i]; g[i][j]=1; }else{ f[i][j]=f[i-1][j]; } } } } vector<int> ve; for(int i=n,j=v;i>0;i--) { if(g[i][j]) { ve.push_back(a[i]); j=j-a[i]; } } for(int i=ve.size()-1;i>=0;i--) { printf("%d ",ve[i]); } printf("sum:%d\\n",f[n][v]); } return 0; } 完全背包完全背包求解的问题和01背包类似,但物品的选取变为无限个,即可以无限选取, 可以想到,每件物品最多能拿floor(V/ci)个,那么可以把这些分成一个个只能拿一次 的物品,这样就又转换为01背包问题,但是这样会很慢,需要优化(多重背包会讲到二进制优化)。 这里我们采用O(VN)的算法(详见背包九讲) D:Piggy-Bank 题意:给定一个背包,用给定的n个物品装满,可无限选用,使价值最低 分析:完全背包模板题,但是有“需要装满”的条件(1*) (1*):引用背包九讲的观点(做笔记做笔记): 要求恰好装满背包,那么在初始化时除了 F[0] 为 0 ,其它 F[1..V ] 均设为 −∞ ,这样就可以保证最终得到的 F[V ] 是一种恰好装满背包的最优解。 如果并没有要求必须把背包装满,而是只希望价格尽量大,初始化时应该将 F[0..V ] 全部设为 0 。 代码如下: View Codec++ #include <cstdio> #include <algorithm> #include <string.h> using namespace std; const int maxn=500+10; int f[10000+5],T,S,F,v,n,p[maxn],w[maxn]; int main() { scanf("%d",&T); while(T--) { scanf("%d %d %d",&S,&F,&n); for(int i=1;i<=n;i++) scanf("%d %d",&p[i],&w[i]); v=F-S; for(int i=1;i<=v;i++) f[i]=1e8; f[0]=0; for(int i=1;i<=n;i++) { for(int j=w[i];j<=v;j++) { f[j]=min(f[j],f[j-w[i]]+p[i]); } } if(f[v]==1e8) { printf("This is impossible.\\n"); }else { printf("The minimum amount of money in the piggy-bank is %d.\\n",f[v]); } } } 多重背包和完全背包不同的是,多重背包的每个物品有有限个的数目。当然,我们仍然可以转换成 01背包求解。但囿于复杂度过高,不得不进行优化。这里介绍二进制优化(见E题)。 E:Dividing 题意:给出一堆面额分别为1-6的硬币,问是否可以分为等额的两堆 分析:是否能分为等额的两堆<==>能否装满总面额一半的面额 ==>(必须装满的)多重背包问题。==>分解成01背包复杂度O(V*sigema(C)) ==>如何降低复杂度(1*) (1*):假设一个物品只能取l件,将l分解成2^0+2^1+...+2^n+M,表示将l件物品分解 成系数为2^0,2^1,...,2^n,M的u件物品,比如说,13件价值为1的物品,分解成1,2,4,6 这4件物品,价格分别为1*1,2*1,4*1,6*1。 为什么这个算法是正确的呢?我自己的理解是,首先我们知道每个数都可以由若干个 2的幂次数相加得到。然后,举刚刚的13件物品为例,分解之后,这4个价格已经可以装满, (1,13)中,3=1+2,5=1+4,7=1+2+4,8=2+6,9=1+2+6,10=4+6,11=1+4+6,12=2+4+6. 大家可以发现,每个价值都可以由这4个面额搭配得到,并且都只选用一次。算法正确性显然。 (我知道不严谨,别打我我是弟弟) 具体看代码主体: View Codec++ #include <iostream> #include <cstdio> #include <algorithm> #include <string.h> #include <cmath> using namespace std; const int N=5e5; int c[7],f[N],t=0; int main() { while(1) { int sum=0; for(int i=1;i<=6;i++) { scanf("%d",&c[i]); sum+=i*c[i]; } if(sum==0) break; if(sum%2!=0) { printf("Collection #%d:\\n",++t); printf("Can't be divided.\\n\\n"); continue; } sum/=2; for(int i=1;i<=sum;i++) f[i]=-1e9; f[0]=0; for(int i=1;i<=6;i++) { int l=min(c[i],sum/i); for(int k=1;l>0;k*=2) { if(k>l) k=l; l-=k; for(int j=sum;j>=i*k;j--) { f[j]=max(f[j],f[j-i*k]+i*k); } } } printf("Collection #%d:\\n",++t); if(f[sum]<0) { printf("Can't be divided.\\n\\n"); }else{ printf("Can be divided.\\n\\n"); } } return 0; } F:Coins 大意:给出硬币面额及每种硬币的个数,求从1到m能凑出面额的个数。 分析:仅需要知道是否能得到该面额==>多重背包的可行性问题 如果我们仍选用上一道题的解法,即便采用二进制优化,O(V*sigema(log(ci))*M) 也承担不起,但是我们可以想到这道题不需要知道装满该容量的最大价值(实际上上一 题也不需要,只是为了学习二进制优化而故意使用的),这里面或许可以优化。 ==>设 F[i,j] 表示“用了前 i 种物品填满容量为 j 的背包后,最多还剩 下几个第 i 种物品可用”,如果 F[i,j] = −1 则说明这种状态不可行,若可行应满足 0 ≤ F[i,j] ≤ M i 具体看代码 (=´ω`=):(第二层的两个循环可以合并,减少一些常数复杂度,不然编译器选的不好就会T(逃~) View Codec++ #include <iostream> #include <algorithm> #include <cstdio> #include <string.h> using namespace std; const int N=1e5+5; int f[N],n,m,cut[105],w[105]; int main() { while(~scanf("%d %d",&n,&m)&&n&&m) { for(int i=1;i<=n;i++) {scanf("%d",&w[i]);} for(int i=1;i<=n;i++) {scanf("%d",&cut[i]);} for(int j=1;j<=m;j++) {f[j]=-1;} f[0]=0; for(int i=1;i<=n;i++) { for(int j=0;j<=m;j++) { if(f[j]>=0) { f[j]=cut[i]; }else{ f[j]=-1; } } for(int j=0;j<=m-w[i];j++) { if(f[j]>0) { f[j+w[i]]=max(f[j+w[i]],f[j]-1); } } } int ans=0; for(int j=1;j<=m;j++) { if(f[j]>=0) ans++; } printf("%d\\n",ans); } return 0; } 数塔问题dp中比较简单的类型,最简单的即金字塔问题,从顶端到底部的最短距离 初学者容易误认为可以用贪心解决(对就是我)o(╥﹏╥)o C:Unidirectional TSP 大意:一个人从左边移动到右边,可以走三个方向(可实现第一排移动到最下面一排的穿越) 分析:数塔问题的进阶,瞎搞搞可能也能过(划掉),因为要输出字典序最小,关键是如何巧妙 解决可以穿越的问题(1*)。 1*:对于当前节点,把可以移动到该节点的三个坐标的列数记录下来,进行一个排序,这样 可以把上下两排的判断一般化,不需要特判,缩减代码量,也不容易写错 具体代码: View Codec++ #include <cstdio> #include <iostream> #include <algorithm> #include <string.h> using namespace std; const int N=105; const int INF=1e9; int m,n,d[15][N],nxt[15][N],a[15][N]; int main() { while(~scanf("%d %d",&m,&n)) { for(int i=0;i<m;i++) { for(int j=0;j<n;j++) { scanf("%d",&a[i][j]); } } int ans=INF,first=0; for(int j=n-1;j>=0;j--) { for(int i=0;i<m;i++) { if(j==n-1) d[i][j]=a[i][j]; else { int rows[3]={i,i-1,i+1}; if(i==0) rows[1]=m-1; if(i==m-1) rows[2]=0; sort(rows,rows+3); d[i][j]=INF; for(int k=0;k<3;k++) { int v=d[rows[k]][j+1]+a[i][j]; if(v<d[i][j]) {d[i][j]=v;nxt[i][j]=rows[k];} } } if(j==0&&d[i][j]<ans){ans=d[i][j];first=i;} } } printf("%d",first+1); for(int i=nxt[first][0],j=1;j<n;i=nxt[i][j],j++) printf(" %d",i+1); printf("\\n%d\\n",ans); } return 0; } LCS(最长公共子序列) && LIS(最长单调子序列)比较简单的DP类型,具体的讲解可以参考 这里(常见的动态规划问题分析与求解) G:Common Subsequence 大意:求两字符串的最长公共子序列 分析:裸的LCS模板,直接放代码啦(>ω・* )ノ 代码如下: View Codec++ #include <iostream> #include <string.h> #include <algorithm> #include <cmath> using namespace std; int f[1005][1005]; char c1[1005],c2[1005]; int main() { while(~scanf("%s %s",c1,c2)) { int len1=strlen(c1),len2=strlen(c2); memset(f,0,sizeof(f)); for(int i=1;i<=len1;i++) { for(int j=1;j<=len2;j++) { if(c1[i-1]==c2[j-1]) { f[i][j]=f[i-1][j-1]+1; }else { f[i][j]=max(f[i-1][j],f[i][j-1]); } } } printf("%d\\n",f[len1][len2]); } return 0; } H:最少拦截系统 大意:导弹拦截系统可递减高度地拦截导弹,求拦截全部导弹最少需要多少套系统。 分析:正向用LIS我们可以知道系统可以拦截的最多的导弹是多少,反过来便可以 求得最少需要多少套系统(此处需要Dilworth定理,不会,先挖个坑) 直接上代码: View Codec++ #include <cstdio> #include <string.h> #include <iostream> #include <algorithm> using namespace std; const int maxn = 1e5+10; int n,f[maxn],num[maxn],d[maxn]; int main() { //freopen("1020.txt","r",stdin); while(~scanf("%d",&n)){ int t=0; for(int i=1;i<=n;i++) { scanf("%d",&num[i]); f[i]=1; //cout<<num[i]<<endl; for(int j=t;j>=1;j--) { //cout<<num[j]<<" "; if(num[i]>num[d[j]]){ f[i]=max(f[i],f[d[j]]+1); } } t=max(t,f[i]); d[f[i]]=i; } cout<<t<<endl; } return 0; } I:Super Jumping! Jumping! Jumping! 大意:从起点往终点跳跃,只能到比当前格子严格小的格子,不能回头,分数为跳过的格子价值总和。 分析:LIS裸题,只不过f[i]从表示长度改为表示sum 代码来咯||ヽ( ̄▽ ̄)ノミ|Ю View Codec++ #include <cstdio> #include <algorithm> #include <string.h> #include <iostream> using namespace std; const int maxn = 1000+10; int f[maxn],n,a[maxn]; int main() { while(~scanf("%d",&n)&&n) { for(int i=1;i<=n;i++) scanf("%d",&a[i]); memset(f,0,sizeof(f)); int ans=-1; for(int i=1;i<=n;i++) { f[i]=a[i]; for(int j=1;j<i;j++) { if(a[j]<a[i]) { f[i]=max(f[i],f[j]+a[i]); } } ans=max(ans,f[i]); } printf("%d\\n",ans); } return 0; } 稍微复杂M:Hills 大意: 分析: View Codec++ #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define N 5005 typedef long long ll; int n,a[N]; ll f[N][N>>1][2]; int main() { scanf("%d",&n); int i,j; for(i=1;i<=n;i++) scanf("%d",&a[i]); int lim=(n+1)>>1; memset(f,0x3f,sizeof(f)); f[0][0][0]=0; f[1][0][0]=f[1][1][1]=0; for(i=2;i<=n;i++) { f[i][0][0]=0; for(j=1;j<=i;j++) { f[i][j][1]=min(f[i-1][j-1][0]+max(0,a[i-1]-a[i]+1), f[i-2][j-1][1]+max(0,a[i-1]-min(a[i-2],a[i])+1)); f[i][j][0]=min(f[i-1][j][0],f[i-1][j][1]+max(0,a[i]-a[i-1]+1)); } } for(i=1;i<=lim;i++) printf("%lld ",min(f[n][i][0],f[n][i][1])); return 0; } N:Jury Compromise 大意: 分析:","categories":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"动态规划","slug":"动态规划","permalink":"http://WigginsLi.github.io/categories/动态规划/"}],"tags":[{"name":"DP","slug":"DP","permalink":"http://WigginsLi.github.io/tags/DP/"},{"name":"背包","slug":"背包","permalink":"http://WigginsLi.github.io/tags/背包/"},{"name":"数塔问题","slug":"数塔问题","permalink":"http://WigginsLi.github.io/tags/数塔问题/"},{"name":"LIS","slug":"LIS","permalink":"http://WigginsLi.github.io/tags/LIS/"},{"name":"LCS","slug":"LCS","permalink":"http://WigginsLi.github.io/tags/LCS/"}],"keywords":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"动态规划","slug":"动态规划","permalink":"http://WigginsLi.github.io/categories/动态规划/"}]},{"title":"待补 < DFS+BFS > 2019 GDUT Winter Training I","slug":"dfs-bfs-training","date":"2019-02-26T16:00:13.000Z","updated":"2019-03-15T05:29:26.975Z","comments":true,"path":"2019/02/27/dfs-bfs-training/","link":"","permalink":"http://WigginsLi.github.io/2019/02/27/dfs-bfs-training/","excerpt":"看到各位dalao都把题解和cf补完了,心里慌得一批,马上gun来补题解总结(〃’▽’〃)专题链接","text":"看到各位dalao都把题解和cf补完了,心里慌得一批,马上gun来补题解总结(〃’▽’〃)专题链接 BFS(Breadth-first search):顾名思义,广度优先搜索即优先各个方向向外扩张,而不是专注于某个方向上的深度(DFS), 既然如此,那么BFS一层一层向外延伸的特点可用于求解最短路问题 下面几道例题调教一下吧( ̄▽ ̄)/ K:Safe Path 大意:给定nm(2<=nm<=2e5)的地图,可上下左右移动,求从S点到F点的最短路,同时地图有若干怪兽M,其(曼哈顿距离)[]为d(0<=d<=2e5)的点均不可走 分析:如果题目没有怪兽的条件,那么就是一道BFS裸题,除了需要考虑如何存地图(*1). 存在怪兽的情况下,对于每个怪兽如果全部暴力标记不能走的点,那么存在一群怪兽聚在一起 时,会对于某些点进行多次标记的多余操作,直接T.(*2) 解决办法: (*1)·2e5*2e5二维数组开不下,可以转换为2e5一维数组. ·因为输入数据是字符串,也可以考虑string存图.(STL大法好) 整数的话可以用vector. (*2)我们回想bfs的性质,如果从每个牛同时开始BFS进行标记,那么某两个牛的重合区域 将只会被其中一只牛标记,那么可以保证整张图每个点最多只被标记一次 代码如下代码里面把每个点的4个方位最远点存进去是煞笔操作简洁写法是记录每个怪兽坐标,最后一起丢进queue里面进行BFS即可 View Codec++ #include <iostream> #include <algorithm> #include <cstdio> #include <queue> #include <map> #include <string.h> using namespace std; const int maxn = 2e5+5; string a[maxn]; int n,m,d,sx,sy,fx,fy; int dir[5]={0,-1,0,1,0}; queue<pair<pair<int,int>,int> > q; map<pair<int,int>,int> ha; int main() { scanf("%d %d %d",&n,&m,&d); getchar(); for(int i=0;i<n;i++) { cin>>a[i]; getchar(); } for(int i=0;i<n;i++) { for(int j=0;j<m;j++) { if(a[i][j]=='S') { sx=i,sy=j; }else if(a[i][j]=='F'){ fx=i,fy=j; }else if(a[i][j]=='M') { q.push(make_pair(make_pair(i,j),1)); //shabi操作 if(i+d<n) q.push(make_pair(make_pair(i+d,j),1)),ha[make_pair(i+d,j)]=1; if(i-d>=0) q.push(make_pair(make_pair(i-d,j),1)),ha[make_pair(i-d,j)]=1; if(j+d<n) q.push(make_pair(make_pair(i,j+d),1)),ha[make_pair(i,j+d)]=1; if(j-d>=0) q.push(make_pair(make_pair(i,j-d),1)),ha[make_pair(i,j-d)]=1; ha[make_pair(i,j)]=1; while(!q.empty()) { int x=q.front().first.first; int y=q.front().first.second; q.pop(); for(int k=-1;k<=1;k++) { for(int kk=-1;kk<=1;kk++) { int nx=x+k,ny=y+kk; if(nx>=0&&nx<n&&ny>=0&&ny<m&&!ha.count(make_pair(nx,ny))&&a[nx][ny]!='M'&&abs(nx-i)+abs(ny-j)<=d) { //printf("%d %d %d %d\\n",nx,ny,i,j); a[nx][ny]='O'; ha[make_pair(nx,ny)]=1; q.push(make_pair(make_pair(nx,ny),1)); } } } } } } } /* for(int i=0;i<n;i++) { for(int j=0;j<n;j++) { cout<<a[i][j]; } cout<<endl; } */ if(a[sx][sy]=='S'&&a[fx][fy]=='F') { q.push(make_pair(make_pair(sx,sy),1)); ha[make_pair(sx,sy)]=1; while(!q.empty()){ int x=q.front().first.first; int y=q.front().first.second; int lv=q.front().second; //cout<<x<<" "<<y<<endl; q.pop(); for(int i=0;i<4;i++) { int nx=x+dir[i],ny=y+dir[i+1]; if(nx>=0&&nx<n&&ny>=0&&ny<m&&!ha.count(make_pair(nx,ny))&&a[nx][ny]!='O'&&a[nx][ny]!='M') { if(a[nx][ny]=='F') { cout<<lv<<endl; return 0; } ha[make_pair(nx,ny)]=1; q.push(make_pair(make_pair(nx,ny),lv+1)); } } } cout<<-1<<endl; }else{ printf("-1\\n"); } return 0; } L:Find The Multiple 大意:给定整数n(1<=n<=200),求G=m*n只存在1 or 0,(m<=1e100),输出G(不唯一) 分析:如果按照题意遍历n的倍数 ==> 1e100/1e2=1e98 ==> TLE 反过来想,如果只遍历所有只有1或0的整数,那么约有2^100≈1e30 打表之后发现似乎并不需要这么多,long long就能装下了,不会证明,逃~~ 写法:维护一个queue,先push(1),每次取队头x,x*10和x*10+1继续push进去,直到找到 能整除n的整数,输出,结束 代码如下: View Codec++ #include <iostream> #include <algorithm> #include <cstdio> #include <queue> #include <string.h> using namespace std; typedef long long ll; int n; queue<ll> q; int main() { while(scanf("%d",&n)&&n>0) { while(!q.empty()) q.pop(); q.push(1); while(!q.empty()) { ll u=q.front(); q.pop(); if(u%n==0) { printf("%lld\\n",u); break; } q.push(u*10); q.push(u*10+1); } } return 0; } M:Prime Path 大意:给定一个四位数,每次修改其中一位,求到达一个素数的最短路径 分析:涉及素数,那就先打出素数表 那么可以模拟题意进行BFS,每次取队头,暴力出所有可以换的数字 (注意第一位不能为0),存进队列 代码如下: View Codec++ #include <iostream> #include <algorithm> #include <string.h> #include <queue> #include <cstdio> #include <cmath> using namespace std; const int maxn = 1e5; int T,p[maxn],vis[maxn],s,f; queue<pair<int,int> > q; void init() { memset(p,0,sizeof(p)); int m=sqrt(maxn+0.5); for(int i=2;i<=m;i++) if(!p[i]) { for(int j=i*i;j<=maxn;j+=i) p[j]=1;//1表示不是_(:з」∠)_ } } int fun(int a,int n) { int u=1; for(int i=1;i<n;i++) { a/=10; u*=10; } return a%10*u; } int mypow(int x,int y) { int u=1; for(int i=1;i<=y;i++) { u*=x; } return u; } int main() { init(); scanf("%d",&T); while(T--) { memset(vis,0,sizeof(vis)); scanf("%d %d",&s,&f); int ok=0; while(!q.empty()) q.pop(); q.push(make_pair(s,0)); vis[s]=1; while(!q.empty()) { int nownum=q.front().first; int nowlv=q.front().second; //printf("%d %d\\n",nownum,nowlv); q.pop(); if(nownum==f) { cout<<nowlv<<endl; ok=1; break; } for(int i=1;i<=4;i++) { int c=nownum; c-=fun(c,i); //printf("%d %d\\n",nownum,c); for(int j=0;j<=9;j++) { if(c+j*mypow(10,i-1)>1000&&!vis[c+j*mypow(10,i-1)]&&!p[c+j*mypow(10,i-1)]) { q.push(make_pair(c+j*mypow(10,i-1),nowlv+1)); vis[c+j*mypow(10,i-1)]=1; } } } } if(!ok) { printf("Impossible\\n"); } } return 0; } DFS(Depth-first search)深度优先遍历,有别于广度优先,每次搜索都会“一条路走到黑”,可以用于求解 是否存在可行解,配合回溯、剪枝更美味哦Ψ( ̄∀ ̄)Ψ G:Lake Counting 大意:给定n*m的地图,求w联通块数量,连通的条件是8个方向上有接触. 分析:裸DFS,注意一下标准写法。 代码如下: View Codec++ #include <cstdio> #include <iostream> #include <string.h> #include <algorithm> #include <cstring> using namespace std; const int maxn = 100; int ans=0,n,m,vis[maxn][maxn]; char map[maxn][maxn]; int dir[3]={0,-1,1}; int dfs(int x,int y) { for(int i=0;i<3;i++) { for(int j=0;j<3;j++) { int nx=x+dir[i],ny=y+dir[j]; if(nx>=0&&ny>=0&&nx<n&&ny<m&&!vis[nx][ny]&&map[nx][ny]=='W'){ vis[nx][ny]=1; dfs(nx,ny); } } } return 0; } int main() { memset(vis,0,sizeof(vis)); scanf("%d %d",&n,&m); getchar(); for(int i=0;i<n;i++) { gets(map[i]); } for(int i=0;i<n;i++) { for(int j=0;j<m;j++) { if(!vis[i][j]&&map[i][j]=='W') { ans++; vis[i][j]=1; dfs(i,j); } } } cout<<ans<<endl; return 0; } I:Red and Black 大意:给定N*M的地图,求出起点@能往外走的格子个数 分析:dfs裸题,找到起点,开始dfs递归,标记走过的格子,最后统计一下有多少个 标记即可 代码如下: View Codec++ #include <cstdio> #include <iostream> #include <string.h> #include <algorithm> #include <cstring> using namespace std; const int maxn = 50; int ans=0,n,m,vis[maxn][maxn]; char map[maxn][maxn]; int dir[5]={0,-1,0,1,0}; int dfs(int x,int y) { for(int i=0;i<4;i++) { int nx=x+dir[i],ny=y+dir[i+1]; if(nx>=0&&ny>=0&&nx<n&&ny<m&&!vis[nx][ny]&&map[nx][ny]=='.'){ vis[nx][ny]=1; dfs(nx,ny); } } return 0; } int main() { while(scanf("%d %d",&m,&n)&&n>0&&m>0) { memset(vis,0,sizeof(vis)); getchar(); for(int i=0;i<n;i++) { gets(map[i]); } for(int i=0;i<n;i++) { for(int j=0;j<m;j++) { if(map[i][j]=='@') { vis[i][j]=1; dfs(i,j); } } } ans=0; for(int i=0;i<n;i++) { for(int j=0;j<m;j++) { if(vis[i][j]) { ans++; } } } cout<<ans<<endl; } return 0; } H:Sticks 还没补or2 J:AND Graph 同样没做(/ω\) 后记:专题一的简单总结结束,专题二的DP继续加油啊WiLe蒟蒻∠( °ω°)/","categories":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"图论","slug":"图论","permalink":"http://WigginsLi.github.io/categories/图论/"}],"tags":[{"name":"DFS","slug":"DFS","permalink":"http://WigginsLi.github.io/tags/DFS/"},{"name":"BFS","slug":"BFS","permalink":"http://WigginsLi.github.io/tags/BFS/"}],"keywords":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"},{"name":"图论","slug":"图论","permalink":"http://WigginsLi.github.io/categories/图论/"}]},{"title":"转战vim(附配置)","slug":"vim-use","date":"2019-01-31T17:41:57.000Z","updated":"2019-05-27T12:54:41.336Z","comments":true,"path":"2019/02/01/vim-use/","link":"","permalink":"http://WigginsLi.github.io/2019/02/01/vim-use/","excerpt":"终于痛心疾首下定居心抛弃Dev,陪着打了几年oi的老伙伴已经不适用于ACM了 已经抛弃原生vim(实在丑,颜控捂脸),vscode或者atom的vim模式更好看","text":"终于痛心疾首下定居心抛弃Dev,陪着打了几年oi的老伙伴已经不适用于ACM了 已经抛弃原生vim(实在丑,颜控捂脸),vscode或者atom的vim模式更好看 这几天先是捣鼓了win下的gvim,发现怎么用怎么别扭,只好转虚拟机的Ubuntu,vim简单上手打了一场cf模拟赛(还好只是模拟,不然也是得掉分了)update:还是回到win环境下比较习惯,毕竟很多配置之类的不能共享,而且Linux下又找不到合适的代替常用软件的替代品Orz win下终端的话因为cmd实在太丑了,推荐git bash vim在某些方面非常好用,比如标准模式下’gg=G’自动处理代码缩进,但是某些方面对于我这个win党造成了极大的伤害,Ctrl C、V默认不是复制粘贴,每次按出个”^c”之类的,内心万千草泥马。 下面附上配置 "自动括号匹配 imap {<CR> {<CR>}<ESC>O imap [ []<LEFT> imap ( ()<LEFT> imap < <><LEFT> syntax on set nu set nocompatible set cindent set mouse=a set autoindent set shiftwidth=4 set tabstop=4 set softtabstop=4 set expandtab set cursorline nmap<F2> : vs %<.in <CR> "nmap<F8> : !./%< < %<.in <CR> nmap<F8> : !./%< < %<.in <CR> "nmap<F4> : !gedit % <CR> nmap<F4> : !notepad % <CR> "nmap<F5> : !./%< <CR> nmap<F5> : !%<.exe <CR> nmap<F7> : !g++ % -o %< -O2 -g -Wall <CR> ps:注释掉的是Linux下使用的 以上","categories":[{"name":"Experience","slug":"Experience","permalink":"http://WigginsLi.github.io/categories/Experience/"}],"tags":[{"name":"vim","slug":"vim","permalink":"http://WigginsLi.github.io/tags/vim/"}],"keywords":[{"name":"Experience","slug":"Experience","permalink":"http://WigginsLi.github.io/categories/Experience/"}]},{"title":"< 尺取,二分,三分 > 2019 GDUT Winter Training I","slug":"19_1_16","date":"2019-01-16T20:45:29.000Z","updated":"2019-08-09T11:06:35.106Z","comments":true,"path":"2019/01/17/19_1_16/","link":"","permalink":"http://WigginsLi.github.io/2019/01/17/19_1_16/","excerpt":"寒假集训专题一(对没错本蒟蒻来CtrlCV大法写题解+总结了)专题一(点这里)包括尺取,二分,三分,dfs,bfs(搜索好难(:з」∠))这篇文字就简单讲讲尺取、二分、三分吧 19.3.5 update : 修复了一些排版的小bug","text":"寒假集训专题一(对没错本蒟蒻来CtrlCV大法写题解+总结了)专题一(点这里)包括尺取,二分,三分,dfs,bfs(搜索好难(:з」∠))这篇文字就简单讲讲尺取、二分、三分吧 19.3.5 update : 修复了一些排版的小bug 尺取基本思路:尺取也被称为“毛毛虫法”,顾名思义,在求一段连续子区间时,左右边界进行挪动从而得到解。(这不是废话嘛谁知道你在讲什么)那么接下来从例子入手。 例题一(A题) 大意:是给一段n个正整数的序列,让你找出最短的满足区间和大于S的子序列的长度 对于这道题,我们可以考虑维护l、r表示当前区间的边界,初始l,r指向序列头,对于当前区间,如果区间和仍小于S,那么我们需要使r往前移(就像毛毛虫的头往前挪),直到当前区间和大于等于S,记录下当前区间长度这时候如果r继续前移,那么虽然当前区间满足条件,但长度必定比已经记录的值更长,所以我们需要l前移(毛毛虫的尾巴)。重复上述过程,直到“毛毛虫”爬完整个序列。 代码如下 View Codec++ #include <cstdio> #include <algorithm> #include <iostream> using namespace std; const int maxn = 1e5+10; int T,n,s,num[maxn]; int main() { scanf("%d",&T); while(T--) { scanf("%d %d",&n,&s); for(int i=1;i<=n;i++) { scanf("%d",&num[i]); } int l=1,r=1,sum=num[1],ans=maxn; while(l<=r&&r<=n) { //cout<<sum<<endl; if(sum>=s) { if(sum-num[l]>=s) sum-=num[l++]; else{ //cout<<l<<" "<<r<<endl; ans=min(r-l+1,ans); sum-=num[l++]; } }else{ sum+=num[++r]; } } if(ans==maxn) cout<<0<<endl; else cout<<ans<<endl; } return 0; } 例题二(B题) 大意:是求1-n中哪一段子区间的平方和等于S。按字典序输出全部情况。 基本思路和例题一类似,唯一不同的是需要输出序列,可以考虑用数组记录下l,r,输出答案时再输出即可。 那就直接贴代码了(只是懒得再写一遍题解): View Codec++ #include <cstdio> #include <cmath> #include <algorithm> #include <iostream> using namespace std; typedef long long ll; const int maxn = 1e7; ll n,x[maxn],y[maxn],ans=0; int main() { scanf("%lld",&n); ll l=1,r=1,sum=1,len=sqrt(n)+2; while(l<=r&&r<=len) { //cout<<sum<<endl; if(sum>=n) { if(sum==n) { ++ans; x[ans]=l,y[ans]=r; } sum-=l*l; l++; }else{ r++; sum+=r*r; } } cout<<ans<<endl; for(int i=1;i<=ans;i++) { cout<<y[i]-x[i]+1<<" "; for(int j=x[i];j<=y[i];j++) { cout<<j<<" "; } cout<<endl; } return 0; } 小结:尺取可以在O(n)的时间得出类似的求一段连续子区间的解,虽然适用性不广(至少在我看来,也可能是因为我菜QAQ),不过该用上的时候也会有不错的效果。 二分基本思路:二分的思想最早可以从中学学过的牛顿迭代求根开始接触到,简单来说,假如要在一段单调性唯一(假定是单增)的序列中找到一个值,维护区间边界l,r,每次取l,r中点值与需要找的值作比较,若过大,则右边界左移至中点处,反之则左边界右移到中点处,如此往复,那么在每次折半后,最多只需要log2(n)次便可以检索到解。 下面三道例题: 例题三(C题) 大意:是给出n个位置,选出其中m个位置,使得m个位置之间的最短距离最大。输出最大的最短距离。 这道题可以对距离进行二分,首先对位置排序,对于每个距离,如果能满足可以选择的位置大于等于m个,那么该距离为合法距离。考虑维护区间边界l,r,中点mid,如果mid为合法距离那么区间向右移,如果mid不合法,那么区间应该往左移才能得到合法距离。 具体看代码叭: View Codec++ #include <cstdio> #include <string.h> #include <iostream> #include <algorithm> using namespace std; const int maxn = 1e9+5; int num[100000+5],n,c; int check(int o) { int u=1,now=1; for(int i=2;i<=n;i++) { while(i<=n&&num[i]-num[now]<o) { i++; } if(i<=n) { now=i; u++; } } if(u>=c) { return 1; }else{ return 0; } } int main() { scanf("%d %d",&n,&c); for(int i=1;i<=n;i++) { scanf("%d",&num[i]); } sort(num+1,num+n+1); int l=0,r=maxn; while(l<r-1) { int mid=(l+r)/2; //cout<<l<<" "<<mid<<" "<<r<<endl; if(check(mid)) { l=mid; }else{ r=mid; } } if(check(l))printf("%d\\n",l); else printf("%d\\n",r); return 0; } 例题四(D题) 大意:一根棍子横立与两面墙之间,当棍子受热膨胀后会弯成弓形(可以看做圆弧的一部分),求弯曲的棍子与原棍子的中点的距离。 题目思路不难想,设长度为x,那么根据勾股定理和三角函数就可以列出一系列方程,那么只要对x进行二分,也就是前文提到的牛顿迭代求解的过程,就可以得到解。(然而这道题我交了30发的WA,直到我把评测姬从g++改成c++,wa的一声哭出来) 代码如下: View Codec++ #include <cstdio> #include <string.h> #include <cmath> #include <iostream> #include <algorithm> using namespace std; #define eps 1e-6 double L,LL,n,c; int main() { while(~scanf("%lf %lf %lf",&L,&n,&c)) { if(L<0&&n<0&&c<0) break; LL=L*(1.0+n*c); double R,l=0.0,r=L*0.5,mid; while(r-l>eps) { mid=(l+r)/2.0; if(2 * asin((L / 2) / ((L*L + 4 * mid*mid) / (8 * mid)))*((L*L + 4 * mid*mid) / (8 * mid))>=LL) { r=mid; }else{ l=mid; } } printf("%.3lf\\n",mid); } return 0; } 例题五(F题) 大意:简单二分+交互,具体看原题 CF的特有题,交互很有意思一般也不难,加上fflush(stdout)或者用endl就问题不大了,没看清题可能会吃亏,(手动打码)我才不会告诉你这道题我看错了大于小于号WA了近十发。(专题感觉像划水一样随随便便就交题了太不认真了orz) 代码: View Codec++ #include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> using namespace std; const int maxn = 1e9; int main() { int l=0,r=1e9,mid; while(1) { mid=(l+r)/2; cout<<"Q "<<mid<<endl; string c; cin>>c; cout.flush(); if(c=="=") exit(0); else if(c=="<") { r=mid; }else { l=mid+1; } } return 0; } 小结:二分作为一个常用算法,写起来还不是特别顺手,师兄说cf中间大部分都是二分题啊。 三分例题6:E题 大意:对多个方程$ s_i(x) = ax^2+bx+c $ ,取$ F(x) = max(s_i(x)),x \\in [0,1000] $ 标准三分,不解释啦(ಥ_ಥ) 代码: View Codec++ #include <iostream> #include <algorithm> #include <cstdio> #include <string.h> #include <cmath> using namespace std; const int maxn=1e4+5; int T,n,a[maxn],b[maxn],c[maxn]; int main() { scanf("%d",&T); while(T--) { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d %d %d",&a[i],&b[i],&c[i]); } double l=0,r=1000,mid1,mid2,now1,now2; while(r-l>0.00000000001) { mid1=(l+r)/2; mid2=(mid1+r)/2; now1=a[1]*mid1*mid1+b[1]*mid1+c[1]; now2=a[1]*mid2*mid2+b[1]*mid2+c[1]; for(int i=2;i<=n;i++) { now1=max(now1,a[i]*mid1*mid1+b[i]*mid1+c[i]); now2=max(now2,a[i]*mid2*mid2+b[i]*mid2+c[i]); } //printf("%lf-%lf:%lf=%lf %lf=%lf\\n",l,r,mid1,now1,mid2,now2); if(now1>now2) { l=mid1; }else{ r=mid2; } } printf("%.4lf\\n",now1); } return 0; } 后记:(第一篇总结报告写得像翔一样我也没有办法鸭(:з」∠))","categories":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"}],"tags":[{"name":"二分","slug":"二分","permalink":"http://WigginsLi.github.io/tags/二分/"},{"name":"尺取","slug":"尺取","permalink":"http://WigginsLi.github.io/tags/尺取/"},{"name":"三分","slug":"三分","permalink":"http://WigginsLi.github.io/tags/三分/"}],"keywords":[{"name":"Training","slug":"Training","permalink":"http://WigginsLi.github.io/categories/Training/"}]},{"title":"刷题计划(集训队计划外)","slug":"plan","date":"2019-01-10T12:25:28.000Z","updated":"2019-02-26T09:51:33.890Z","comments":true,"path":"2019/01/10/plan/","link":"","permalink":"http://WigginsLi.github.io/2019/01/10/plan/","excerpt":"这个博客终于成型了一把辛酸泪,马上开始自闭开心刷题鸭 2019.1.10update:补充了杭电入门及poj题单","text":"这个博客终于成型了一把辛酸泪,马上开始自闭开心刷题鸭 2019.1.10update:补充了杭电入门及poj题单 WATER首先HDU水题100得做完吧(:з」∠)——->HDU100水题还有一个杭电入门题单——->hdu入门 然后是洛谷普及组的试炼场(我才不会告诉你是因为我tql菜的真实)——->洛谷普及组 然后的然后,kuangbin带你飞系列,“少水群多刷题” ——kuangbin蒟蒻先clone一个专题一吧emmm——->kuangbin专题一——->kuangbin全系列 进阶就来一个poj题单吧——->poj很好很有层次感 Hard(也许是有生之年系列(大雾))一份codeforces题单——->cf题单 To be continue…","categories":[{"name":"Experience","slug":"Experience","permalink":"http://WigginsLi.github.io/categories/Experience/"}],"tags":[{"name":"计划","slug":"计划","permalink":"http://WigginsLi.github.io/tags/计划/"}],"keywords":[{"name":"Experience","slug":"Experience","permalink":"http://WigginsLi.github.io/categories/Experience/"}]},{"title":"HelloMyDearFriend","slug":"HelloMyDearFriend","date":"2019-01-08T16:49:00.000Z","updated":"2019-03-15T05:28:29.527Z","comments":true,"path":"2019/01/09/HelloMyDearFriend/","link":"","permalink":"http://WigginsLi.github.io/2019/01/09/HelloMyDearFriend/","excerpt":"(~ ̄▽ ̄)~勇士,似乎你发现了未知的领域","text":"(~ ̄▽ ̄)~勇士,似乎你发现了未知的领域 #include <iostream> using namespace std; int main() { cout<<"Hello World!"<<endl; return 0; }","categories":[{"name":"Experience","slug":"Experience","permalink":"http://WigginsLi.github.io/categories/Experience/"}],"tags":[{"name":"Hello","slug":"Hello","permalink":"http://WigginsLi.github.io/tags/Hello/"}],"keywords":[{"name":"Experience","slug":"Experience","permalink":"http://WigginsLi.github.io/categories/Experience/"}]}]}