[{"content":"旗山校区指南 By 枫原.\n​\t岁律既周，寒暑易节。不觉间，诸位负笈入庠，已历一载光阴。 ​\t回首去岁初逢，犹怀克谨克慎之气；今看今朝重聚，已成游刃有余之才。学海扬帆，曾挑灯于伴读之夜；杏坛论道，共执手于同气之连。一载功成，既谢青涩；弱冠始加，终成大器。在此，特向诸位顺利致毕大一学年、学有所成，致以由衷之贺。\n​\t胜友如云，新程续启。值此新象，诸位亦将迁往旗山校区（逃离铜盘那个山沟沟）。\n​\t新居甫至，难免兼怀向往之情、未知之惑。广厦万间，何处安居？泮宫路远，何寻游学？鼎食钟鸣，何方果腹？为使诸君免摸索之仓促，多笃定之从容，特以此文为引，用作指南。且随笔墨，大开新居之画卷；预窥胜景，共启未来之新章。\n图 1：旗山校区地图 （该地图形成时间较早，部分建筑与如今布局有所出入，仅为示意） Part Zero 搬家 以下内容仅代表个人观点，若与 年段通知冲突以年段通知为准\n​\t在介绍旗山校区之前，我须先向大家说明搬迁的一些注意事项。\n​\t校区搬迁一般在第二学期末或第三学期初完成，由学院出资租用货车运送行李。\n​\t有两种搬迁方式（都有可能，具体得等接近期末才知晓，大家提前做好心理准备）：\n​\t方案一\n​\t于最后一科期末考后1-2天由学院统一搬迁至旗山。想提前搬的可以自己找搬家公司（自费），搬完即可离校。注:到时需做好相关登记，否则进不来，不能找有“敞篷车”类型的，要找面包车类型的。\n​\t方案二\n​\t于下学期开学前再搬往旗山（由于旗山宿舍需要大面积粉刷不能放行李的原因），由于下学期新生入学，铜盘宿舍也不让放行李，会期末考后1-2天统一将大家的行李先搬至教学楼，于开学前再搬往旗山（学校及学院出钱）。注:建议能搬回家的同学先搬回家。\n​\t搬迁前，会组织学生干部先行前往旗山校区考察宿舍环境并拍照，供同学们提前了解宿舍情况。\n​\t在搬迁的前一天，学生需将行李整理打包至旅行袋或行李箱中并贴好标签（标签由学院发放），方案一的情况是一般凌晨四点后搬迁工作就将开始，届时大家需要根据志愿者和搬迁工人的引导将行李送至指定位置，低楼层由工人搬至楼下，高楼层由云梯负责运送，无需学生手动搬迁。需要注意的是，导员将为各个宿舍规划搬迁时间，请按照此时间将行李摆放至指定位置，否则可能错过本班车次，导致后续难以寻找行李。行李出发后，学生将由学院安排的大巴车运送至旗山校区，大巴车也是固定时间发车，大家注意发车时间和车牌号（会在段群中通知）。方案二的情况是搬迁时间教学楼时间不会特别早，除直接送至旗山校区改成送至教学楼外，其余情况相差不大，后续等临近开学，才会将大家行李从教学楼搬往旗山。\n​\t行李运送至旗山校区后，工人将根据标签内容将行李运送至指定楼层，未粘贴标签或标签内容不清楚的行李将滞留在一楼，请大家及时寻找自己的行李，若未能找到，可关注段群中的失物招领消息或直接在段群中询问是否有人看到，请不要将行李长时间放置在宿舍外，影响交通，增大丢失风险。\n​\t有特殊需求的同学可自行搬迁（需提前申请），自行搬迁的同学需要避开统一搬迁时间，且租用的货车不能为敞篷货车，该标准以导员通知为准。\n​\t注:\n​\t1.贵重物品及易碎物品请自行携带，统一搬迁不搬这些。\n​\t2.宿舍安排出来较晚（主要是由于旗山宿舍的空床位难以给出）大家稍安勿躁。\n​\t3.通过计算机学院25级导员消息，一部分暑假要上课的同学（大部分为数智班同学），会在期末将行李搬至旗山（方案一），\t借住其他宿舍，等开学前更换宿舍。其余同学根据方案二搬迁。\n​\t4.正常情况导员会在搬迁前召开一个搬迁事宜介绍段会，各位同学不要错过。\nPart One 生活 ​\t欢迎大家来到福州大学旗山校区！\n​\t首先向大家介绍我们的宿舍所在：计算机学院本科生宿舍均在生活区三区（博学苑），从地理位置上来看位于学校的左下角，毗邻玫瑰园、紫荆园两个食堂，靠近教学区西二门，附近的体育设施有：风雨操场、三区操场、三区篮球场，具体可见图1。\n​\t我院宿舍均为四人间，上床下桌（床铺宽90cm，长195cm，架高110cm）、一体卫浴（就是没有干湿分离的意思），独立阳台（虽然很小HaHaHa）。\n​\t具体情况可以参考下图2、3、4：\n图 2：宿舍内部 图 3：阳台 图 4：厕所 入住时可能遇到的问题有： ​\t1、墙壁掉漆\n​\t暑假将会对掉漆严重的宿舍进行重新粉刷，如认为宿舍掉漆严重但未在粉刷名单中，也可提出申请进行粉刷（须知：待粉刷的宿\t舍不能放置行李）。\n​\t2、空调、门锁等设施损坏\n​\t在智汇福大-\u0026gt;公寓报修中选择报修，如修理师傅迟迟未上门，可在对应界面中催促或联系辅导员催促，切勿自行修理。\n​\t3、部分宿舍较脏（特别是厕所）\n​\t如情况不是很严重可以自行清理，如果都是陈年污垢请及时联系清洁人员。\n​\t4、床帘安装困难\n​\t如图5所示，旗山部分宿舍有突起的梁柱，会对窗帘安装造成影响，此时主动弯曲床帘即可。\n图 5：突出房梁 洗衣机 ​\t旗山宿舍未配备洗衣机，需要的同学可自行购买。\n​\t阳台洗手台上配有2个水龙头，其中靠墙壁的水龙头一般可在下方安装洗衣机水管。\n​\t阳台墙上一般会配有插座，但位置较高，部分洗衣机电源线不够长，此时需要额外安装一个排插（请务必注意防水）。\n​\t水龙头、插座位置可见图3\n饮用水 ​\t是的，旗山校区既没有洗衣机也没有饮水机。\n​\t通常来说，有如下几种办法获取饮用水：\n​\t其一，购买热水壶，接自来水自行烧制。\n​\t其二，在超市购买矿泉水。\n​\t其三，订购桶装水。（为避免广告嫌疑，此处不贴出订购渠道）\n违规电器 ​\t除吹风机、热水壶等产品外，可以认为能够制造大量热或制冷的设备一律算作违规电器，特别是电磁炉等厨具。这方面的限制主要还是为大家的安全考虑，希望大家能够理解。宿舍起火案例不少，笔记本烧了就损失1w了。\n晚点名、晚熄灯、门禁 ​\t每栋宿舍楼下均设有门禁（男生宿舍的门禁一般意外损坏了），相对较为安全，门禁可通过刷脸或智汇福大扫码解开。\n​\t生活区、教学区大门每天晚上12点后关闭，进出均需要从小门通过，需刷脸或智汇福大扫码开门。\n​\t须知，辅导员能够看到大门门禁出入记录。\n​\t每晚10点，由各班安全委员或指定的晚点名人员进行晚点名，并上报请假、未归情况，请注意及时回到宿舍，不能及时归宿的，应提前告知晚点名人员。\n​\t每晚10点30分是宿舍熄灯时间，此时应关闭宿舍内部大灯（台灯、阳台大灯、厕所灯不受限制），10点30分至10点40分，将有专门的晚熄灯督导人员提醒晚熄灯。\n快递 ​\t快递地址：福州大学旗山校区博学苑A/B区学生公寓xx号楼xxx室\n​\t旗山校区快递站点较为分散，分别为：\n​\t一区快递服务站（9号楼后）：邮政\n​\t三区快递服务站（36号楼后）：顺丰、京东、申通、圆通、极兔\n​\t学生街快递服务站：中通\n​\t筑安商业中心快递服务站：韵达\n外卖 ​\t三区外卖地址：福州大学旗山校区博学苑A/B区学生公寓xx号楼xxx室\n​\t能送进校时将送至楼下外卖架，不能送进校时此地址的外卖也会送到三区门口。\n​\t白天大部分外卖都可以送到宿舍楼下，晚上九点左右，校外的外卖就不能送进学校了（只能送到三区门口），但食堂的外卖仍可以商家自配。\n​\t须知，校园送会比正常配送慢一些。\n​\t三区门口的外卖点是蟊贼多发地，最好30分钟内去取，不然可能就要饱某人之腹了。\n超市 ​\t和铜盘校区不同，旗山校区的超市一般不具有打印功能。\n​\t三区内部有2家超市：29号楼一楼的兴福兴超市和26号楼下的小超市。\n​\t兴福兴超市品类较为齐全，可以满足全部生活需要，但每天十一点就会关门。\n​\t小超市关门较晚，但仅有饮品、小零食、水果等商品，没有太多选择。\n打印 ​\t生活区和教学区每栋楼的一楼都配有一台自助打印机，扫码即可打印，还可通过小程序获取免费额度。\n​\t三区的25号楼和26号楼下各有一家打印店。\n​\t其中编者常去的是25#的打印店，因为这家手机扫码打印有优惠，另外，这家也可以打印各种尺寸的照片。\n​\t另一家编者并不熟悉。\n​\t在图书馆一楼，还有一家较大的打印店，但较远且自助打印体验较差，在教学区需要打印时可以选择。\nPart Two 通勤 ​\t旗山校区占地3000多亩，各个生活区到教学区路程不尽相同，三区到教学楼的距离属于较远的，故光凭11路公交车较难满足学习生活需求。\n​\t回到图1，从三区到教学楼一般有两条路：\n​\t其一，由三区大门出，自学府南路向北，从西一门进入教学区，经文源路到达教学楼。\n​\t其二，由三区大门出，从西二门进入教学区，经嘉越路、远志路、方镜桥到达教学楼。\n图 1：旗山校区地图 （该地图形成时间较早，部分建筑与如今布局有所出入，仅为示意） 电动车 ​\t我校对电动车实行RFID管制，在校内骑行需要申请电子通行证。\n​\t目前来看，后续已不再新增电动车电子通行证。\n​\t能告诉大家的是，尽量不要购买二手电动车，特别是无法过户的二手电动车，这类车其实就是黑车，无法申请电子通行证。而学长学姐的电动车即使有通行证，也已过期，不要购买，电子通行证将随持有人的毕业而过期。\n自行车/共享单车 ​\t校内有大量共享单车，大家也可以自行购买自行车用于通勤。\n​\t但值得注意的是，校内大部分路径有大量减速带，自行车（特别是共享单车）的骑行体验不佳。\n​\t如果禁电，或许大家也只能买自行车了。\n小白（公交车） ​\t小白是校内小型公交车的简称，因车辆整体呈白色得名。\n​\t可在微信小程序“一步校园”中浏览叫车点位并呼叫，上车后须向司机师傅说明下车点位。\n​\t收费标准：1元/次\n地铁 ​\t在整张地图的右侧，我们可以看到两个地铁口，分别为金屿C、福大C，均属于福州地铁二号线。\n​\t需要注意的是，福州地铁对福州大学生有周末免费的优惠政策，具体申请流程辅导员将在段群中发布。\nPart Three 主要功能性建筑 ​\t这里再次贴出图1。\n图 1：旗山校区地图 （该地图形成时间较早，部分建筑与如今布局有所出入，仅为示意） 食堂 ​\t学生食堂均位于生活区大门附近。\n​\t从距离上来考虑，三区的同学一般在玫瑰园、紫荆园就餐。\n​\t其中，玫瑰园为食惠餐厅（名字就叫这个），所售餐饭相对较为平价，但没有拼好饭便宜。\n​\t紫荆园中有烧烤、麦当劳、瑞幸、蜜雪等招商店铺，有需要的同学要注意点单地址在三区还是一区。\n​\t玫瑰园一楼有一家霸王茶姬和一家塔斯汀。\n​\t丁香园、京元餐厅在一区西1门附近，铜盘的部分商家在这里也有分档口。\n学生街 ​\t自西2或西1门出，由学府南路向北就可以来到福州大学学生街。\n​\t学生街主营业务为餐饮，包含超市、眼镜店、电动车等杂货门类。\n​\t该区域较为复杂，编者去的次数不多（吗？），留待同学们自行探索。\n运动场地（校园跑） ​\t由生活区向右看可以看到，我校运动场地集中于中南部，即西2门附近，距离三区较近。\n​\t其中能够进行校园跑的场地有：第一田径场（一区）、第二田径场（三区）、风雨操场（三区）\n​\t在西2门附近，还有网球馆、篮球场等设施。\n​\t第二田径场右侧为宏晖文体综合馆，内含羽毛球场、乒乓球场，但经常被各种活动占用，且场地较为难抢。\n​\t体测一般在第一田径场进行。\n校医院 ​\t西2门进门左拐就可以看到我校的校医院。\n​\t校医院科室其实较为齐全，但由于地理位置偏僻，人流量并不大，轻微的跌打损伤或感冒等病症可以就近前往校医院解决。\n素拓 ​\t在三区通往教学楼的第二条路径上，方镜桥上方，是我校的素质拓展中心。\n​\t部分活动可能在此处举办，此外，大部分校级学生组织的办公室/活动室均在此处。\n​\t在素拓门口有一间青年之家，提供良好的自行环境，而且知道的人不多，但偶尔会有活动在此处举办。\n图书馆 ​\t再向上看，可以看到我校图书馆，位于教学区中心。\n​\t官网：福州大学图书馆\n​\t图书馆开放时间：开放时间-福州大学图书馆\n​\t分布：图书分布-福州大学图书馆\n​\t需要注意的是，图书馆需要通过考试才能借阅书籍和使用自习室，考试需在微信公众号“福州大学图书馆”中完成。\n​\t在图书馆的中部有一家饮品店——大梦书屋，该店铺提供较好的自习环境，陈设优美，但价格死贵。\n教学楼 ​\t环绕图书馆一圈的这些建筑就是我校主要的教学楼，我院大部分课程在这些地方教授。\n​\t自西向东共有七幢教学楼：西三至西一、中楼、东一至东三，该区域路线较为复杂，同学们需要在正式开学前进行踩点，否则可能开学第一天就迷路导致迟到——————是的我第一天迟到了但是是因为睡过头了。\n​\t到东三和到西三上课、到二楼和五楼上课所需时间不尽相同，同学们要提前规划好起床时间，以免迟到。\n晋江楼/学习中心 ​\t学校的最右侧，在东大门附近有一幢鹤立鸡群的建筑——晋江楼。\n​\t其实晋江楼是我校的研究生科研中心，但对本科生来说，最重要的功能是位于晋江楼4-5楼的学习中心。学习中心内提供数千个自习空间，也包含讨论室（难申请）、博士单间（没资格），有部分沙发坐。部分竞赛（主要是数学建模）也会在此举行。\n​\t在智汇福大-\u0026gt;综合预约中预约晋江楼学习空间。\n​\t另外，晋江楼一楼有我校的一些展览，感兴趣的同学可以抽空去看看。\n院楼 ​\t我院院楼就在晋江楼上方，图中标注数计学院（现在不叫这个名了），从下到上分别为2号楼、3号楼。\n​\t2号楼主要为我院的一些行政机构，具体如下表：\n部门名称 办公地点 办公室电话 党委办公室 计算机2#217 22865157 行政办公室（一） 计算机2#218 22866359 科研办公室 计算机2#218 22865160 行政办公室（二）/资料室 计算机2#209 22865166/22866355 本科教学办公室 计算机2#203 22865161/22865169 研究生教学办公室 计算机2#202 22865174/22866353 团委办公室 计算机2#103 22866669 学工办公室 计算机2#102 22866354 工会办公室 计算机2#105 22865173 实验中心办公室 计算机3#406 22865175 ​\t对于本科生，最重要的是位于2#102的学工（辅导员）办公室和位于2#203的教学办。\n​\t3号楼低楼层主要包含我院的一些科创团队，如ACM、昆冈和我院的一些实验室。\n​\t从3号楼三楼开始主要为我院的实验中心，我们的专业课实验和实践课都在这里完成。\n物理实验教学中心 ​\t在我院院楼上方，图中标注物信学院的位置是我校的物理实验教学中心，主要承担大学物理实验教学的功能。\n​\t该中心大家应该不陌生，但请注意这是离生活区最远的教学中心，注意时间规划。\nPart Four 结语 ​\t希望大家能够尽快适应旗山校区的生活，然后快点毕业找一个955的工作内推学长我（bushi~\n​\t祝大家前程似锦！\n​\n","date":"2026-06-10T00:00:00Z","permalink":"https://DustLi-DC.github.io/p/guide/","title":"旗山校区指南"},{"content":"专题七 图论 102400226 石华波\nVjudge账号:DustLi\n问题一\tStockbroker Grapevine 问题陈述 城市准备建造一个物流中心，今后所有物流都会由中心向其他物流站分配。 现在有n个站点，站点之间存在着单向的道路，走每条道路都需要花费一些时间，在这些站点中选择一个，使分配最高效。\n给出站点数目和道路情况，请输出选择的物流站点编号以及今后所需的分配时间。假设从中转中心同时向其他站分发，所需的分配时间指的是最后一个物流站点接受到物流所花费的总时间。\n解题思路 Floyd求最短路。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #include \u0026lt;iostream\u0026gt; #include \u0026lt;vector\u0026gt; #include \u0026lt;climits\u0026gt; using namespace std; using ll=long long; int N; void Floyd(vector\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt; \u0026amp;Dis,int N) { for (int k =1;k\u0026lt;=N;++k) for (int i=1;i\u0026lt;=N;++i) for (int j=1;j\u0026lt;=N;++j) Dis[i][j]=min(Dis[i][j],Dis[i][k]+Dis[k][j]); } int main() { int N; while(cin\u0026gt;\u0026gt;N\u0026amp;\u0026amp;N) { vector\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt; Dis(N+1,vector\u0026lt;int\u0026gt;(N+1,1e5)); for(int i=1;i\u0026lt;=N;++i) { int M; cin\u0026gt;\u0026gt;M; Dis[i][i]=0; while(M--) { int P,T; cin\u0026gt;\u0026gt;P\u0026gt;\u0026gt;T; Dis[i][P]=T; } } Floyd(Dis,N); int Min=1e5,Pos=0; for(int i=1;i\u0026lt;=N;++i) { int Max=-1e5; for(int j=1;j\u0026lt;=N;++j) Max=max(Dis[i][j],Max); if(Max\u0026lt;Min) { Min=Max; Pos=i; } } if(Min==1e5) cout\u0026lt;\u0026lt;\u0026#34;disjoint\u0026#34;\u0026lt;\u0026lt;endl; else cout\u0026lt;\u0026lt;Pos\u0026lt;\u0026lt;\u0026#34; \u0026#34;\u0026lt;\u0026lt;Min\u0026lt;\u0026lt;endl; } return 0; } 问题二\t树的直径 问题陈述 一棵树的直径就是这棵树上存在的最长路径。现在有一棵 nn 个节点的树，现在想知道这棵树的直径包含的边的个数是多少？\n如图所示的数据，这棵树的直径为 (1−2−3−6−9)(1−2−3−6−9) 这条路径，包含的边的个数为 44 ，所以答案是 44 。\n解题思路 双DFS，由第一个节点找到离它最远的节点，再找到离此节点最远的节点即可。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include \u0026lt;iostream\u0026gt; #include \u0026lt;vector\u0026gt; #include \u0026lt;climits\u0026gt; using namespace std; using ll=long long; int N,C,Cnt; int Dis[100001]; vector\u0026lt;int\u0026gt; Pat[100001]; void DFS(int U,int F) { for (int V:Pat[U]) { if(V==F) continue; Dis[V]=Dis[U]+1; if(Dis[V]\u0026gt;Dis[C]) { C=V; Cnt++; } DFS(V,U); } } int main() { cin\u0026gt;\u0026gt;N; for(int i=1;i\u0026lt;N;i++) { int S,E; cin\u0026gt;\u0026gt;S\u0026gt;\u0026gt;E; Pat[S].push_back(E); Pat[E].push_back(S); } DFS(1,0); Dis[C]=0; Cnt=0; DFS(C,0); cout\u0026lt;\u0026lt;Cnt\u0026lt;\u0026lt;endl; return 0; } 问题三\tInvitation Cards 问题陈述 在电视时代，参加剧院表演的人并不多。马利丁西亚的古董喜剧演员意识到了这一点。他们希望传播剧院，尤其是古董喜剧。他们印制了包含所有必要信息和节目安排的邀请卡。许多学生被雇来在公众中分发这些邀请函。每位学生志愿者被分配了一个特定的公交站，他们整天待在那里，向乘坐公交的人发放邀请函。学生们参加了一门特殊课程，学习如何影响他人，以及影响与抢劫之间的区别。 交通系统非常特别：所有线路都是单向的，并且连接恰好两个站点。公交车每半小时从出发站出发，载着乘客。到达目的地后，公交车空车返回出发站，等待下一个整点半，例如 X:00 或 X:30，其中 \u0026lsquo;X\u0026rsquo; 表示小时。两个站点之间的交通费用由特殊表格给出，并且需当场支付。线路的规划方式是，每个往返（即从同一站点出发并返回）都经过一个中央检查站（CCS），每位乘客都必须经过全面检查，包括身体扫描。\n所有 ACM 学生成员每天早上从 CCS 出发。每位志愿者都要前往一个预定的站点邀请乘客。志愿者的数量与站点数量相同。一天结束时，所有学生都会返回 CCS。你需要编写一个计算机程序，帮助 ACM 最小化每天为其员工的交通费用。\n解题思路 不解绑cin会超时很多很多很多很多很多。\n反向建边，先从CCS找去各个站点的最短路，再从各个站点找回CCS。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 #include \u0026lt;iostream\u0026gt; #include \u0026lt;vector\u0026gt; #include \u0026lt;queue\u0026gt; #include \u0026lt;climits\u0026gt; using namespace std; using ll=long long; const int MaxN=1000001; int P,Q; int Dist1[MaxN],Dist2[MaxN]; void Dijkstra(vector\u0026lt;vector\u0026lt;pair\u0026lt;int, int\u0026gt;\u0026gt;\u0026gt; \u0026amp;v,int Dist[MaxN]) { for(int i=1;i\u0026lt;=P;i++) Dist[i]=INT_MAX; Dist[1] = 0; priority_queue\u0026lt;pair\u0026lt;int,int\u0026gt;,vector\u0026lt;pair\u0026lt;int,int\u0026gt;\u0026gt;,greater\u0026lt;\u0026gt;\u0026gt; Q; Q.push({0, 1}); while(!Q.empty()) { auto [w, S] = Q.top(); Q.pop(); if(Dist[S]\u0026lt;w) continue; for(auto\u0026amp; [E,weight]:v[S]) { if(Dist[S]+weight\u0026lt;Dist[E]) { Dist[E]=Dist[S]+weight; Q.push({Dist[E],E}); } } } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int T; cin\u0026gt;\u0026gt;T; while(T--) { cin\u0026gt;\u0026gt;P\u0026gt;\u0026gt;Q; vector\u0026lt;vector\u0026lt;pair\u0026lt;int,int\u0026gt;\u0026gt;\u0026gt; E1(P+1),E2(P+1); for(int i=1;i\u0026lt;=Q;i++) { int St,En,Va; cin\u0026gt;\u0026gt;St\u0026gt;\u0026gt;En\u0026gt;\u0026gt;Va; E1[St].push_back({En,Va}); E2[En].push_back({St,Va}); } Dijkstra(E1,Dist1); Dijkstra(E2,Dist2); int Ans=0; for(int i=1;i\u0026lt;=P;i++) Ans+=Dist1[i]+Dist2[i]; cout\u0026lt;\u0026lt;Ans\u0026lt;\u0026lt;endl; } return 0; } 问题4\t战略游戏 问题陈述 他要建立一个古城堡，城堡中的路形成一棵无根树。他要在这棵树的结点上放置最少数目的士兵，使得这些士兵能瞭望到所有的路。\n注意，某个士兵在一个结点上时，与该结点相连的所有边将都可以被瞭望到。\n请你编一程序，给定一树，帮 Bob 计算出他需要放置最少的士兵。\n解题思路 树形DP？\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include\u0026lt;iostream\u0026gt; #include\u0026lt;vector\u0026gt; #include\u0026lt;climits\u0026gt; using namespace std; using ll=long long; const int N = 1501; int n; vector\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt; Links(N); int DP[N][2]; void DFS(int u,int Fa) { DP[u][1]=1; DP[u][0]=0; for (auto v:Links[u]) { if (v==Fa) continue; DFS(v,u); DP[u][0]+=DP[v][1]; DP[u][1]+=min(DP[v][1],DP[v][0]); } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cin\u0026gt;\u0026gt;n; for (int i=1;i\u0026lt;=n;i++) { int x,k,y; cin\u0026gt;\u0026gt;x\u0026gt;\u0026gt;k; for (int j=1;j\u0026lt;=k;j++) { cin\u0026gt;\u0026gt;y; Links[x].push_back(y); Links[y].push_back(x); } } DFS(1,-1); cout\u0026lt;\u0026lt;min(DP[1][0],DP[1][1])\u0026lt;\u0026lt;endl; return 0; } 问题五\t飞行路线 问题陈述 Alice 和 Bob 现在要乘飞机旅行，他们选择了一家相对便宜的航空公司。该航空公司一共在 nn 个城市设有业务，设这些城市分别标记为 00 到 n−1n−1，一共有 mm 种航线，每种航线连接两个城市，并且航线有一定的价格。\nAlice 和 Bob 现在要从一个城市沿着航线到达另一个城市，途中可以进行转机。航空公司对他们这次旅行也推出优惠，他们可以免费在最多 kk 种航线上搭乘飞机。那么 Alice 和 Bob 这次出行最少花费多少？。\n解题思路 分层图最短路。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 #include \u0026lt;iostream\u0026gt; #include \u0026lt;vector\u0026gt; #include \u0026lt;queue\u0026gt; #include \u0026lt;climits\u0026gt; using namespace std; using ll=long long; const int MAXN=1e5+5; const int MAXK=25; int Dis[MAXN][MAXK]; bool Vis[MAXN][MAXK]; int n,m,k,s,t; vector\u0026lt;vector\u0026lt;pair\u0026lt;int,int\u0026gt;\u0026gt;\u0026gt; Edge; void Dijkstra() { fill_n(*Dis,MAXN*MAXK,INT_MAX); Dis[s][0]=0; priority_queue\u0026lt;vector\u0026lt;int\u0026gt;,vector\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt;,greater\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt;\u0026gt; pq; pq.push({0,s,0}); while(!pq.empty()) { vector\u0026lt;int\u0026gt; state=pq.top(); pq.pop(); int u=state[1],cnt=state[2]; if(Vis[u][cnt]) continue; Vis[u][cnt]=true; for(auto\u0026amp; edge:Edge[u]) { int v=edge.first,w=edge.second; if(cnt\u0026lt;k \u0026amp;\u0026amp; Dis[v][cnt+1]\u0026gt;Dis[u][cnt]) { Dis[v][cnt+1]=Dis[u][cnt]; pq.push({Dis[v][cnt+1],v,cnt+1}); } if(Dis[v][cnt]\u0026gt;Dis[u][cnt]+w) { Dis[v][cnt]=Dis[u][cnt]+w; pq.push({Dis[v][cnt],v,cnt}); } } } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cin\u0026gt;\u0026gt;n\u0026gt;\u0026gt;m\u0026gt;\u0026gt;k; cin\u0026gt;\u0026gt;s\u0026gt;\u0026gt;t; s++;t++; Edge.resize(n+1); for(int i=0;i\u0026lt;m;++i) { int u,v,w; cin\u0026gt;\u0026gt;u\u0026gt;\u0026gt;v\u0026gt;\u0026gt;w; u++;v++; Edge[u].emplace_back(v,w); Edge[v].emplace_back(u,w); } Dijkstra(); int Ans=INT_MAX; for(int i=0;i\u0026lt;=k;++i) Ans=min(Ans,Dis[t][i]); cout\u0026lt;\u0026lt;Ans\u0026lt;\u0026lt;endl; return 0; } 问题六\t二叉苹果树 问题陈述 有一棵苹果树，如果树枝有分叉，一定是分二叉（就是说没有只有一个儿子的结点）\n这棵树共有 NN 个结点（叶子点或者树枝分叉点），编号为 1∼N1∼N，树根编号一定是 11。\n我们用一根树枝两端连接的结点的编号来描述一根树枝的位置。下面是一颗有 44 个树枝的树：\n1 2 3 4 5 2 5 \\ / 3 4 \\ / 1 现在这颗树枝条太多了，需要剪枝。但是一些树枝上长有苹果。\n给定需要保留的树枝数量，求出最多能留住多少苹果。\n解题思路 树形DP。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #include\u0026lt;iostream\u0026gt; #include\u0026lt;vector\u0026gt; #include\u0026lt;climits\u0026gt; using namespace std; using ll=long long; const int MaxN=105; int n,m,DP[MaxN][MaxN],Br[MaxN]; vector\u0026lt;pair\u0026lt;int,int\u0026gt;\u0026gt; Edge[MaxN]; void DFS(int u, int fa) { for (auto \u0026amp;edge:Edge[u]) { int v=edge.first; int w=edge.second; if(v==fa) continue; DFS(v,u); Br[u]+=Br[v] + 1; for (int j=min(Br[u],m);j\u0026gt;=1;j--) for (int k=min(Br[v],j-1);k\u0026gt;=0;k--) DP[u][j]=max(DP[u][j],DP[u][j-k-1]+DP[v][k]+w); } } int main() { cin\u0026gt;\u0026gt;n\u0026gt;\u0026gt;m; for(int i=1;i\u0026lt;n;++i) { int u,v,w; cin\u0026gt;\u0026gt;u\u0026gt;\u0026gt;v\u0026gt;\u0026gt;w; Edge[u].emplace_back(v,w); Edge[v].emplace_back(u,w); } DFS(1,-1); cout\u0026lt;\u0026lt;DP[1][m]\u0026lt;\u0026lt;endl; return 0; } 学习总结 本专题内容主要为图论，通过学习，提高了自己的能力。\n","date":"2025-02-19T00:00:00Z","permalink":"https://DustLi-DC.github.io/p/topic-seven/","title":"专题七 图论"},{"content":"专题六 动态规划 102400226 石华波\nVjudge账号:DustLi\n问题一\t自然数的拆分问题 问题陈述 给出一个长度为 n 的序列 a，选出其中连续且非空的一段使得这段和最大。\n解题思路 上学期啥时候做的了所以语言还是C（）。\n如果当前项和之前所选序列之和大于当前项，那就将当前项加入序列中，反之截断序列，由当前项开始。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include\u0026lt;stdio.h\u0026gt; #include\u0026lt;limits.h\u0026gt; int main(){ static int N,A,B,MAX=INT_MIN; scanf(\u0026#34;%d\u0026#34;,\u0026amp;N); scanf(\u0026#34;%d\u0026#34;,\u0026amp;A); B=A; MAX=B; while(--N) { scanf(\u0026#34;%d\u0026#34;,\u0026amp;A); B=B+A\u0026gt;A?B+A:A; MAX=B\u0026gt;MAX?B:MAX; } printf(\u0026#34;%d\u0026#34;,MAX); return 0; } 问题二\t采药 问题陈述 辰辰是个天资聪颖的孩子，他的梦想是成为世界上最伟大的医师。为此，他想拜附近最有威望的医师为师。医师为了判断他的资质，给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说：“孩子，这个山洞里有一些不同的草药，采每一株都需要一些时间，每一株也有它自身的价值。我会给你一段时间，在这段时间里，你可以采到一些草药。如果你是一个聪明的孩子，你应该可以让采到的草药的总价值最大。”\n如果你是辰辰，你能完成这个任务吗？\n解题思路 0-1背包。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include\u0026lt;iostream\u0026gt; #include\u0026lt;climits\u0026gt; using namespace std; using ll = long long; int T,M; int W[101],Val[101],DP[1001];; int main() { cin\u0026gt;\u0026gt;T\u0026gt;\u0026gt;M; for(int i=1;i\u0026lt;=M;i++) cin\u0026gt;\u0026gt;W[i]\u0026gt;\u0026gt;Val[i]; for(int i=1;i\u0026lt;=M;i++) for(int j=T;j\u0026gt;=W[i];j--) DP[j]=max(DP[j],DP[j-W[i]]+Val[i]); cout\u0026lt;\u0026lt;DP[T]\u0026lt;\u0026lt;endl; return 0; } 问题三\t宝物筛选 问题陈述 终于，破解了千年的难题。小 FF 找到了王室的宝物室，里面堆满了无数价值连城的宝物。\n这下小 FF 可发财了，嘎嘎。但是这里的宝物实在是太多了，小 FF 的采集车似乎装不下那么多宝物。看来小 FF 只能含泪舍弃其中的一部分宝物了。\n小 FF 对洞穴里的宝物进行了整理，他发现每样宝物都有一件或者多件。他粗略估算了下每样宝物的价值，之后开始了宝物筛选工作：小 FF 有一个最大载重为 W 的采集车，洞穴里总共有 n 种宝物，每种宝物的价值为 v**i，重量为 w**i，每种宝物有 m**i 件。小 FF 希望在采集车不超载的前提下，选择一些宝物装进采集车，使得它们的价值和最大。\n解题思路 二进制优化的0-1背包。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include\u0026lt;iostream\u0026gt; #include\u0026lt;climits\u0026gt; using namespace std; using ll = long long; int W,M,cnt=0; int Wei[100001],Val[100001],DP[100001];; int Value,Weight,Amount; int main() { cin\u0026gt;\u0026gt;M\u0026gt;\u0026gt;W; for(int i=1;i\u0026lt;=M;i++) { cin\u0026gt;\u0026gt;Value\u0026gt;\u0026gt;Weight\u0026gt;\u0026gt;Amount; int k=1; while(Amount\u0026gt;k) { Amount-=k; Wei[++cnt]=k*Weight; Val[cnt]=k*Value; k\u0026lt;\u0026lt;=1; } if(Amount) { Wei[++cnt]=Weight*Amount; Val[cnt]=Value*Amount; } } for(int i=1;i\u0026lt;=cnt;i++) for(int j=W;j\u0026gt;=Wei[i];j--) DP[j]=max(DP[j],DP[j-Wei[i]]+Val[i]); cout\u0026lt;\u0026lt;DP[W]\u0026lt;\u0026lt;endl; return 0; } 问题4\t最长公共子序列 问题陈述 给出 1,2,…,n 的两个排列 P1 和 P2 ，求它们的最长公共子序列。\n解题思路 把LCS转化为LIS。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include\u0026lt;iostream\u0026gt; #include\u0026lt;climits\u0026gt; using namespace std; using ll = long long; int A[100001], B[100001]; int N; int DP[100001],Map[100001]; int cnt=0; int main() { cin\u0026gt;\u0026gt;N; for(int i=1;i\u0026lt;=N;i++) { cin\u0026gt;\u0026gt;A[i]; Map[A[i]]=i; DP[i]=INT_MAX; } for(int i=1;i\u0026lt;=N;i++) cin\u0026gt;\u0026gt;B[i]; for(int i=1;i\u0026lt;=N;i++) { if(Map[B[i]]\u0026gt;DP[cnt]) DP[++cnt]=Map[B[i]]; else { int L=0,R=cnt,Mid; while(L\u0026lt;R) { Mid=(L+R)/2; if(DP[Mid]\u0026gt;Map[B[i]]) R=Mid; else L=Mid+1; } DP[L]=min(DP[L],Map[B[i]]); } } cout\u0026lt;\u0026lt;cnt; return 0; } 问题五\tKevin and Puzzle 问题陈述 凯文喜欢逻辑谜题。\n他和n个同学玩了一个排成一行的游戏。第i个从左边数的人说他们左边有ai个说谎者（不包括他们自己）。\n每个同学要么诚实，要么说谎，且有一个限制：没有两个说谎者可以站在一起。诚实的同学总是说真话。说谎者可以说真话或谎言，这意味着他们的陈述被认为是不可靠的。\n凯文想要确定不同的游戏配置数量，结果对998244353取模。两个配置被认为不同，如果至少有一个同学在一个配置中是诚实的，而在另一个配置中是说谎者。\n解题思路 先将DP数组初始化为0（一开始没发现要初始化用了数组后面懒得改成vector了）,(0,0)的点为1。\n处理第i人时，如果说的是真话，那就有ai个骗子，如果是假话，就有a(i-1)+1个骗子（第i个人假话第i-1人必为真话)。\n以0表示说真话，1表示说假话。\n所以DP[i][1]可以直接等于DP[i-1][0]；\n对于DP[i][0]，ai=a(i-1)，那么等于其本身与DP[i-1][0]的和。\n如果ai=a(i-2)+1，那么等于其本身与DP[i-2][0]的和（这种情况下i-1说假话）。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include\u0026lt;iostream\u0026gt; #include\u0026lt;climits\u0026gt; #include\u0026lt;cstring\u0026gt; using namespace std; using ll = long long; const ll Mod=998244353; int t,n; void solve() { } int main() { cin\u0026gt;\u0026gt;t; while(t--) { cin\u0026gt;\u0026gt;n; int Per[n+1]; Per[0]=0; for(int i=1;i\u0026lt;=n;i++) cin\u0026gt;\u0026gt;Per[i]; ll DP[n+1][2]; memset(DP,0,sizeof(DP)); DP[0][0]=1; for(int i=1;i\u0026lt;=n;i++) { DP[i][1]=DP[i-1][0]; if(Per[i]==Per[i-1]) DP[i][0]=(DP[i][0]+DP[i-1][0])%Mod; if(i\u0026gt;=2\u0026amp;\u0026amp;Per[i]==Per[i-2]+1) DP[i][0]=(DP[i][0]+DP[i-2][0])%Mod; } cout\u0026lt;\u0026lt;(DP[n][0]+DP[n][1])%Mod\u0026lt;\u0026lt;endl; } return 0; } 问题六\tWorld is Mine 问题陈述 Alice 和 Bob 在玩一个游戏。一开始有 nn 个蛋糕，第 ii 个蛋糕的美味值为 aia**i。\nAlice 和 Bob 轮流吃蛋糕，Alice 先开始：\n在她的回合中，Alice 选择并吃掉任何剩余蛋糕，其美味值严格大于她之前吃过的所有蛋糕中的最大美味值。注意在第一回合中，她可以选择任何蛋糕。 在他的回合中，Bob 选择任何剩余蛋糕并吃掉。 游戏在当前玩家无法吃到合适的蛋糕时结束。设 xx 为Alice吃掉的蛋糕数量。然后，Alice 想要最大化 xx，而Bob 想要最小化 xx。\n找出如果两个玩家都以最佳方式玩游戏时，Alice 将吃掉多少个蛋糕。\n解题思路 Alice需要递增地吃蛋糕，由于每种蛋糕可能不止出现一次，所以用cnt数组来存储每种蛋糕出现的次数——刚好可以由此递增地访问。\n对于同一种蛋糕，Bob需要吃掉所有的这种蛋糕，否则相当于没吃，如果Bob没吃，那么Alice的最优策略必然包含了这种蛋糕。\n并且，Bob不能吃超过n/2个蛋糕（两人是轮流吃的）。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include \u0026lt;iostream\u0026gt; #include \u0026lt;vector\u0026gt; #include \u0026lt;climits\u0026gt; using namespace std; using ll=long long; int t,n,tas; int main() { cin\u0026gt;\u0026gt;t; while (t--) { cin\u0026gt;\u0026gt;n; vector\u0026lt;int\u0026gt; cnt(n+1); for(int i=1;i\u0026lt;=n;i++) { cin\u0026gt;\u0026gt;tas; cnt[tas]++; } vector DP(n+1,vector(n+1,LLONG_MAX-1)); DP[0][0]=0; for(int i=1;i\u0026lt;=n;i++) { for(int j=0;j\u0026lt;=n\u0026gt;\u0026gt;1;j++) { if(cnt[i]==0) DP[i][j]=min(DP[i][j],DP[i-1][j]); else { if(j+cnt[i]\u0026lt;=n\u0026gt;\u0026gt;1\u0026amp;\u0026amp;j+cnt[i]\u0026lt;=DP[i-1][j]) DP[i][j+cnt[i]]=min(DP[i][j+cnt[i]],DP[i-1][j]); DP[i][j]=min(DP[i][j],DP[i-1][j]+1); } } } ll ans=LLONG_MAX-1; for(int i=0;i\u0026lt;=n\u0026gt;\u0026gt;1;i++) ans=min(ans,DP[n][i]); cout\u0026lt;\u0026lt;ans\u0026lt;\u0026lt;endl; } return 0; } 学习总结 本专题内容主要为动态规划，通过学习，提高了自己的能力。\n","date":"2025-02-16T00:00:00Z","permalink":"https://DustLi-DC.github.io/p/topic-six/","title":"专题六 动态规划"},{"content":"专题五 搜索 102400226 石华波\nVjudge账号:DustLi\n问题一\t自然数的拆分问题 问题陈述 任何一个大于 1 的自然数 n，总可以拆分成若干个小于 n 的自然数之和。现在给你一个自然数 n，要求你求出 n 的拆分成一些数字的和。每个拆分后的序列中的数字从小到大排序。然后你需要输出这些序列，其中字典序小的序列需要优先输出。\n解题思路 DFS。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include\u0026lt;iostream\u0026gt; using namespace std; using ll = long long; int Pr[11]={1}; int N; void Print(int M) { for(int i=1;i\u0026lt;M;i++) cout\u0026lt;\u0026lt;Pr[i]\u0026lt;\u0026lt;\u0026#39;+\u0026#39;; if(M!=1) cout\u0026lt;\u0026lt;Pr[M]\u0026lt;\u0026lt;endl; } void DFS(int T) { for(int i=Pr[T-1];i\u0026lt;=N;i++) { Pr[T]=i; N-=i; if(N==0) Print(T); else DFS(T+1); N+=i; } } int main() { cin\u0026gt;\u0026gt;N; DFS(1); return 0; } 问题二\t填涂颜色 问题陈述 由数字 0 组成的方阵中，有一任意形状的由数字 1 构成的闭合圈。现要求把闭合圈内的所有空间都填写成 2。例如：6×6 的方阵（n=6），涂色前和涂色后的方阵如下：\n如果从某个 0 出发，只向上下左右 4 个方向移动且仅经过其他 0 的情况下，无法到达方阵的边界，就认为这个 0 在闭合圈内。闭合圈不一定是环形的，可以是任意形状，但保证闭合圈内的 0 是连通的（两两之间可以相互到达）。\n解题思路 ”寻找闭合圈中的0“是一个比较难的操作，不妨转化为”寻找与边界联通的0并标记“。\n另外，可以先将所有的0染为2，然后将与边界联通的2染回0。\n以Dir数组存储转向数对,BFS。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #include\u0026lt;iostream\u0026gt; #include \u0026lt;queue\u0026gt; using namespace std; using ll = long long; int Mar[31][31]; int Dir[5][5]={{0,0},{0,1},{0,-1},{-1,0},{1,0}}; queue\u0026lt;pair\u0026lt;int,int\u0026gt;\u0026gt; Pos; int main() { int N; cin\u0026gt;\u0026gt;N; for(int i=1;i\u0026lt;=N;i++) for(int j=1;j\u0026lt;=N;j++) cin\u0026gt;\u0026gt;Mar[i][j]; for(int i=0;i\u0026lt;=N+1;i++) for(int j=0;j\u0026lt;=N+1;j++) if(Mar[i][j]==0) Mar[i][j]=2; Pos.push(make_pair(0,0)); while(!Pos.empty()) { int x=Pos.front().first; int y=Pos.front().second; Pos.pop(); Mar[x][y]=0; for(int i=1;i\u0026lt;=4;i++) if(Mar[x+Dir[i][0]][y+Dir[i][1]]==2) Pos.push(make_pair(x+Dir[i][0],y+Dir[i][1])); } for(int i=1;i\u0026lt;=N;i++) { for(int j=1;j\u0026lt;=N;j++) { cout\u0026lt;\u0026lt;Mar[i][j]\u0026lt;\u0026lt;\u0026#34; \u0026#34;; } cout\u0026lt;\u0026lt;endl; } return 0; } 问题三\t素数密度 问题陈述 古老的显示屏是由 N×M 个像素（Pixel）点组成的。一个像素点的位置是根据所在行数和列数决定的。例如 P(2,1) 表示第 2 行第 1 列的像素点。那时候，屏幕只能显示黑与白两种颜色，人们用二进制 0 和 1 来表示。0 表示黑色，1 表示白色。当计算机发出一个指令：P(x,y)=1，则屏幕上的第 x 行第 y 列的阴极射线管就开始工作，使该像素点显示白色，若 P(x,y)=0，则对应位置的阴极射线管不工作，像素点保持黑色。在某一单位时刻，计算机以 N×M 二维 01 矩阵的方式发出显示整个屏幕图像的命令。\n例如，屏幕是由 3×4 的像素点组成，在某单位时刻，计算机发出如下命令：\n000001011110\n对应屏幕显示应为：\n假设放大后，一个格子表示一个像素点。\n由于未知的原因，显示黑色的像素点总是受显示白色的像素点的影响——可能是阴极射线管工作的作用。并且，距离越近，影响越大。这里的距离定义如下：\n设有像素点 P1(x1,y1) 和像素点 P2(x2,y2)，则它们之间的距离 D(P1,P2)=∣x1−x2∣+∣y1−y2∣。\n在某一时刻，计算机发出显示命令后，科学家们期望知道，每个像素点和其最近的显示白色的像素点之间的最短距离是多少——科学家们保证屏幕上至少有一个显示白色的像素点。\n上面的例子中，像素 P(1,1) 与最近的白色像素点之间的距离为 3，而像素 P(3,2) 本身显示白色，所以最短距离为 0。\n解题思路 BFS，对每个黑块处理来找到距离最近的白块操作次数太多，易TLE，不妨从白块出发，依次找到距离最近的白块的距离为1、2\u0026hellip;的黑块。注意检查坐标是否合法。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 #include\u0026lt;iostream\u0026gt; #include \u0026lt;queue\u0026gt; #include \u0026lt;string.h\u0026gt; using namespace std; using ll = long long; int N,M; int Mar[190][190]; int Dis[190][190]; bool Vis[190][190]; int Dir[5][5]={{0,0},{0,1},{0,-1},{-1,0},{1,0}}; queue\u0026lt;pair\u0026lt;int,int\u0026gt; \u0026gt; Pos; bool Ava(int x,int y) { return x\u0026gt;=1\u0026amp;\u0026amp;x\u0026lt;=N\u0026amp;\u0026amp;y\u0026gt;=1\u0026amp;\u0026amp;y\u0026lt;=M; } int main() { cin\u0026gt;\u0026gt;N\u0026gt;\u0026gt;M; char c; for(int i=1;i\u0026lt;=N;i++) { for(int j=1;j\u0026lt;=M;j++) { cin\u0026gt;\u0026gt;c; Mar[i][j]=c-\u0026#39;0\u0026#39;; if(Mar[i][j]==1) { Dis[i][j]=0; Vis[i][j]=true; Pos.push(make_pair(i,j)); } } } while(!Pos.empty()) { int x=Pos.front().first; int y=Pos.front().second; Pos.pop(); for(int i=1;i\u0026lt;=4;i++) { int xi=x+Dir[i][0]; int yi=y+Dir[i][1]; if(Ava(xi,yi)\u0026amp;\u0026amp;!Vis[xi][yi]) { Pos.push(make_pair(xi,yi)); Vis[xi][yi]=true; Dis[xi][yi]=Dis[x][y]+1; } } } for(int i=1;i\u0026lt;=N;i++) { for(int j=1;j\u0026lt;=M;j++) cout\u0026lt;\u0026lt;Dis[i][j]\u0026lt;\u0026lt;\u0026#34; \u0026#34;; cout\u0026lt;\u0026lt;endl; } return 0; } 问题4\t健康的荷斯坦奶牛 问题陈述 农民 John 以拥有世界上最健康的奶牛为傲。他知道每种饲料中所包含的牛所需的最低的维他命量是多少。请你帮助农夫喂养他的牛，以保持它们的健康，使喂给牛的饲料的种数最少。\n给出牛所需的最低的维他命量，输出喂给牛需要哪些种类的饲料，且所需的饲料剂量最少。\n维他命量以整数表示，每种饲料最多只能对牛使用一次，数据保证存在解。\n解题思路 DFS，数目未知选用Vector容器。\n其中：\nif (!Ans.empty() \u0026amp;\u0026amp; Cho.size() \u0026gt;= Ans.size()) return;\n用于剪枝，防止数据量过大超时。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include\u0026lt;iostream\u0026gt; #include \u0026lt;queue\u0026gt; using namespace std; using ll = long long; int V,G; int VatN[26]; int VatS[16][26]; vector\u0026lt;int\u0026gt; Cho; vector\u0026lt;int\u0026gt; Ans; bool Check() { for(int i=1;i\u0026lt;=V;i++) { int Sum=0; for(auto j:Cho) Sum+=VatS[j][i]; if(Sum\u0026lt;VatN[i]) return false; } return true; } void DFS(int x) { if (!Ans.empty() \u0026amp;\u0026amp; Cho.size() \u0026gt;= Ans.size()) return; if(x\u0026gt;G) { if(Check()\u0026amp;\u0026amp;(Ans.empty()||Cho.size()\u0026lt;Ans.size())) Ans=Cho; return; } Cho.push_back(x); DFS(x+1); Cho.pop_back(); DFS(x+1); } int main() { cin\u0026gt;\u0026gt;V; for(int i=1;i\u0026lt;=V;i++) cin\u0026gt;\u0026gt;VatN[i]; cin\u0026gt;\u0026gt;G; for(int i=1;i\u0026lt;=G;i++) for(int j=1;j\u0026lt;=V;j++) cin\u0026gt;\u0026gt;VatS[i][j]; DFS(1); cout\u0026lt;\u0026lt;Ans.size()\u0026lt;\u0026lt;\u0026#39; \u0026#39;; for(auto i:Ans) cout\u0026lt;\u0026lt;i\u0026lt;\u0026lt;\u0026#39; \u0026#39;; return 0; } 问题五\tGRZ-Ridges and Valleys 问题陈述 给定一个 n×n 的网格状地图，每个方格 (i,j) 有一个高度 w**ij。如果两个方格有公共顶点，则它们是相邻的。\n定义山峰和山谷如下：\n均由地图上的一个连通块组成； 所有方格高度都相同； 周围的方格（即不属于山峰或山谷但与山峰或山谷相邻的格子）高度均大于山谷的高度，或小于山峰的高度。 求地图内山峰和山谷的数量。特别地，如果整个地图方格的高度均相同，则整个地图既是一个山谷，也是一个山峰。\n解题思路 BFS，八联通，注意Dir中应有八个方向。\n处理BFS时，将与目前元素高度相等的元素加入队列，并标记已访问，对于与当前元素高度不同的元素，若当前元素较大，则当前联通块不可能为山谷，反之，则当前联通块不可能为山峰。最后计数输出。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #include\u0026lt;iostream\u0026gt; #include \u0026lt;queue\u0026gt; using namespace std; using ll = long long; int N; int CntP,CntV; int Dir[9][9]={{0,0},{-1,1},{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1},{-1,0}}; int Mar[1001][1001]; bool Vis[1001][1001]; queue\u0026lt;pair\u0026lt;int, int\u0026gt;\u0026gt; Pos; bool Ava(int i,int j) { return i\u0026gt;=1\u0026amp;\u0026amp;j\u0026gt;=1\u0026amp;\u0026amp;i\u0026lt;=N\u0026amp;\u0026amp;j\u0026lt;=N; } void BFS() { bool isPeak=true; bool isValley=true; while(!Pos.empty()) { int x=Pos.front().first; int y=Pos.front().second; Pos.pop(); for(int i=1;i\u0026lt;=8;i++) { int xi=x+Dir[i][0]; int yi=y+Dir[i][1]; if(Ava(xi,yi)) { if(Mar[xi][yi]==Mar[x][y]\u0026amp;\u0026amp;!Vis[xi][yi]) { Pos.push(make_pair(xi,yi)); Vis[xi][yi]=true; } if(Mar[xi][yi]\u0026gt;Mar[x][y]) isPeak=false; if(Mar[xi][yi]\u0026lt;Mar[x][y]) isValley=false; } } } if(isPeak) CntP++; if(isValley) CntV++; } int main() { cin\u0026gt;\u0026gt;N; for(int i=1;i\u0026lt;=N;i++) for(int j=1;j\u0026lt;=N;j++) cin\u0026gt;\u0026gt;Mar[i][j]; for(int i=1;i\u0026lt;=N;i++) { for(int j=1;j\u0026lt;=N;j++) { if(!Vis[i][j]) { Pos.push(make_pair(i,j)); BFS(); } } } cout\u0026lt;\u0026lt;CntP\u0026lt;\u0026lt;\u0026#34; \u0026#34;\u0026lt;\u0026lt;CntV\u0026lt;\u0026lt;endl; return 0; } 问题六\tDFS 问题陈述 一个如下的 6×6 的跳棋棋盘，有六个棋子被放置在棋盘上，使得每行、每列有且只有一个，每条对角线（包括两条主对角线的所有平行线）上至多有一个棋子。\n上面的布局可以用序列 2 4 6 1 3 5 来描述，第 i 个数字表示在第 i 行的相应位置有一个棋子，如下：\n行号 1 2 3 4 5 6\n列号 2 4 6 1 3 5\n这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。 并把它们以上面的序列方法输出，解按字典顺序排列。 请输出前 3 个解。最后一行是解的总个数。\n解题思路 DFS，同一条主对角线上的元素横纵坐标之和相等，同一条副对角线上的元素横纵坐标之差相等，故两个一维数组即可存储对角线的占用状态，再开两个一维数组用于存储列的占用状态和当前方案。\nCnt\u0026lt;=3时输出本次方案，最后输出总方案数。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #include\u0026lt;iostream\u0026gt; #include \u0026lt;queue\u0026gt; using namespace std; using ll = long long; int N; int Cnt; bool MDia[27]; bool SDia[27]; bool Col[27]; int Ans[14]; void DFS(int x) { if(x\u0026gt;N) { Cnt++; if(Cnt\u0026lt;=3) { for(int i=1;i\u0026lt;=N;i++) cout\u0026lt;\u0026lt;Ans[i]\u0026lt;\u0026lt;\u0026#34; \u0026#34;; cout\u0026lt;\u0026lt;endl; } return; } for(int i=1;i\u0026lt;=N;i++) { if(!(Col[i]||MDia[x+i]||SDia[x-i+N])) { Ans[x]=i; Col[i]=true; MDia[x+i]=true; SDia[x-i+N]=true; DFS(x+1); Col[i]=false; MDia[x+i]=false; SDia[x-i+N]=false; } } } int main() { cin\u0026gt;\u0026gt;N; DFS(1); cout\u0026lt;\u0026lt;Cnt\u0026lt;\u0026lt;endl; return 0; } 学习总结 本专题内容主要为搜索，通过学习，提高了自己的能力。\n","date":"2025-02-13T00:00:00Z","permalink":"https://DustLi-DC.github.io/p/topic-five/","title":"专题五 搜索"},{"content":"专题四 数论 102400226 石华波\nVjudge账号:DustLi\n问题一\t有理数取余 问题陈述 给出一个有理数 c = $\\frac{a}{b}$，求$c mod 19260817$的值。\n这个值被定义为 $bx ≡ a (mod 19260817)$ 的解。\n输入 一共两行。\n第一行，一个整数 $a$。 第二行，一个整数 $b$。\n输出 一个整数，代表求余后的结果。如果无解，输出 Angry!。\n解题思路 设$19260817$为$M$，那么$bx ≡ a(mod M)$,根据同余方程的性质，可得出$b\\frac{x}{a} ≡ 1(modM)$,将$\\frac{x}{a}$视为一个整体，设为$z$，那么,$bz ≡ 1(modM)$，这是一个比较常见的同余方程，由于$a$已知，只需要求出$z$的值，就可以求出$x$。\n同时，为了避免$a、b$过大，在读入时不断对$M$取余。\n得出的$z$可能是个负数，通过$ z=(z%M+M)%M$处理，保证最后的结果为正数。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #include\u0026lt;algorithm\u0026gt; #include\u0026lt;iostream\u0026gt; #include\u0026lt;tuple\u0026gt; #define ll long long using namespace std; const ll Mod=19260817; tuple\u0026lt;ll,ll,ll\u0026gt; ExGcd(ll a,ll b) { if(b==0) return {a,1,0}; auto [G,x1,y1]=ExGcd(b,a%b); ll x=y1; ll y=x1-a/b*y1; return {G,x,y}; } ll GetMod() { ll Res=0; char ch=getchar(); while(!isdigit(ch)\u0026amp;\u0026amp;ch!=EOF) ch=getchar(); while(isdigit(ch)) { Res=Res*10+ch-\u0026#39;0\u0026#39;; Res%=Mod; ch=getchar(); } return Res; } int main() { ll a,b; a=GetMod(); b=GetMod(); if(b==0) cout\u0026lt;\u0026lt;\u0026#34;Angry!\u0026#34;\u0026lt;\u0026lt;endl; else { auto [G,x,y]=ExGcd(b,Mod); x=(x%Mod+Mod)%Mod; ll ans=a*x%Mod; cout\u0026lt;\u0026lt;ans\u0026lt;\u0026lt;endl; } return 0; } 问题二\tMinimal Coprime 问题陈述 给定$ [l,r]$，一个正整数的区间，找出包含在$ [l,r]$中的最小互质区间的数量。\n解题思路 显然，$x$与$x+1$是互质的，最小互质区间必定为$[x,x+1]$，所以在区间$[l,r]$中，总共有$r-l$个最小互质区间。\n这里特判$[1,1]$，与其他$[x,x]$类型的区间不同，$1$与$1$是互质的。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include\u0026lt;iostream\u0026gt; #define ll long long using namespace std; int main() { int N; cin\u0026gt;\u0026gt;N; while(N--) { int a,b; cin\u0026gt;\u0026gt;a\u0026gt;\u0026gt;b; if(a==b\u0026amp;\u0026amp;a==1) cout\u0026lt;\u0026lt;1\u0026lt;\u0026lt;endl; else cout\u0026lt;\u0026lt;b-a\u0026lt;\u0026lt;endl; } return 0; } 问题三\t素数密度 问题陈述 给定 $L,R$，请计算区间 $[L,R]$中素数的个数。\n$1≤L≤R≤2^{31}，R-L≤10^6$\n解题思路 题目数据范围极大，直接用线性筛也会超时。\n但是，题目告知$L,R$之差不超过$10^6$，我们可以尝试标记区间中的每一个合数。\n$R$小于$INT_MAX$，因为合数必定可以表示为数个质数的积，且一定有至少一个因数$≤\\sqrt{R}$，所以我们只需要筛出$[1,\\sqrt{INT_MAX}]$范围内的质数，然后再用这些质数通过筛法来标记区间内合数，就可以求出区间内质数的个数。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include\u0026lt;iostream\u0026gt; #define ll long long using namespace std; int Prime[50001]; bool isComposite[50001]; bool isCompositeLR[1000001]; int cnt=1; void LinearSieve(){ for(int i=2;i\u0026lt;=50000;i++) { if(!isComposite[i]) Prime[cnt++]=i; for(int j=1;i*Prime[j]\u0026lt;=50000;j++) { isComposite[i*Prime[j]]=true; if(i%Prime[j]==0) break; } } } int main() { LinearSieve(); int l,r; cin\u0026gt;\u0026gt;l\u0026gt;\u0026gt;r; l+=l==1; int ans=0; for(int i=1;i\u0026lt;cnt;i++) { if(Prime[i]\u0026gt;r) break;; int p=Prime[i]; ll SL=(l+p-1)/p*p\u0026gt;2*p?(l+p-1)/p*p:2*p; for(ll j=SL;j\u0026lt;=r;j+=p) isCompositeLR[j-l+1]=true; } for(int i=1;i\u0026lt;=r-l+1;i++) { ans+=!isCompositeLR[i]; } cout\u0026lt;\u0026lt;ans\u0026lt;\u0026lt;endl; return 0; } 问题4\t最大公约数和最小公倍数问题 问题陈述 输入两个正整数 $x_0,y_0$，求出满足下列条件的 $P,Q$ 的个数：\n$P,Q$ 是正整数。 要求$P,Q$ 以 $x_0$ 为最大公约数，以$y_{0}$ 为最小公倍数。 试求：满足条件的所有可能的$P,Q$ 的个数。\n解题思路 $GCD$和$LCM$有一条性质，$GCD×LCM=a×b$，所以，$x_0×y_0=P×Q$，设$x_0×y_0=M$，我们只需要在小于$M$的范围内搜寻能够整除$M$的数，然后验证这个数与$M$除以这个数的商的最大公约数是不是$x_0$即可。\n由于同一对$P,Q$调换顺序算作另一个解，每次处理时令$ans+2$，遇到$x_0=y_0$时，在最终答案上$-1$即可。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include\u0026lt;iostream\u0026gt; #define ll long long using namespace std; ll Gcd(ll a,ll b) { if(b==0) return a; return Gcd(b,a%b); } int main() { ll n,m; int ans=0; cin\u0026gt;\u0026gt;n\u0026gt;\u0026gt;m; if(m==n) ans--; ll s=n*m; for(ll i=1;i*i\u0026lt;=s;i++) if(s%i==0\u0026amp;\u0026amp;Gcd(i,s/i)==n) ans+=2; cout\u0026lt;\u0026lt;ans\u0026lt;\u0026lt;endl; return 0; } 问题五\tLongest Subsequence 问题陈述 给你一个包含 $a$ 个元素的数组 $n$，以及一个数字 $m$。考虑数组 $a$的某个子序列，并计算其元素的最小公倍数$(LCM)$的值。将最小公倍数表示为$l$。找出任意一个值为 $l ≤ m$ 的最长子序列 $a$。\n子序列是从 $a$ 中删除一些元素后得到的数组。可以删除零个或所有元素。\n空数组的最小公倍数等于 $1$。\n解题思路 使用多个数组，$Src$存储原始数据，数组下标即为数据序号。$Tms$存储每个数字出现的次数，避免重复处理浪费时间，用$Len$来存放其下标对应数字所对应的子序列。\n处理数据时，可以忽略大于$m$的数据。\n通过筛的方法找到最大的$l$之后，直接遍历一遍$Src$，输出能够整除$l$的数据的下标即可。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #include\u0026lt;iostream\u0026gt; #define ll long long using namespace std; const int MaxN=1e6+1; int n,m; int Src[MaxN]; int Tms[MaxN]; int Len[MaxN]; int main() { cin\u0026gt;\u0026gt;n\u0026gt;\u0026gt;m; for(int i=1;i\u0026lt;=n;i++) { cin\u0026gt;\u0026gt;Src[i]; if(Src[i]\u0026lt;=m) Tms[Src[i]]++; } for(int i=1;i\u0026lt;=m;i++) { if(Tms[i]==0) continue; for(int j=i;j\u0026lt;=m;j+=i) { Len[j]+=Tms[i]; } } int MaxL=0; int MaxCnt=0; for(int i=1;i\u0026lt;=m;i++) { if(Len[i]\u0026gt;MaxCnt) { MaxCnt=Len[i]; MaxL=i; } } if(MaxL\u0026gt;0) { cout\u0026lt;\u0026lt;MaxL\u0026lt;\u0026lt;\u0026#34; \u0026#34;\u0026lt;\u0026lt;MaxCnt\u0026lt;\u0026lt;endl; for(int i=1;i\u0026lt;=n;i++) if(MaxL%Src[i]==0) cout\u0026lt;\u0026lt;i\u0026lt;\u0026lt;\u0026#34; \u0026#34;; } else cout\u0026lt;\u0026lt;1\u0026lt;\u0026lt;\u0026#34; \u0026#34;\u0026lt;\u0026lt;0\u0026lt;\u0026lt;endl; return 0; } 问题六\tCommon Generator 对于两个整数$x$和$y(x,y\\geq2)$，我们将说$x$是$y$的一个生成器当且仅当$x$可以通过执行以下操作若干次(可能为零)转换为$y$：\n选择一个除数$d(d\\geq2)$，然后将$x$增加$d$。 例如， $3$是$8$的一个生成器，因为我们可以执行以下操作：$\\begin{aligned}3\u0026amp;\\overset{d=3}{\\longrightarrow}6\\\u0026amp;\\overset{d=2}{\\longrightarrow}8;\\end{aligned}$ $4$是$10$的一个生成器，因为我们可以执行以下操作：$\\begin{aligned}4\u0026amp;\\overset{d=4}{\\longrightarrow}8\\\u0026amp;\\overset{d=2}{\\longrightarrow}10;\\end{aligned}$ $5$不是$6$的一个生成器，因为我们无法通过上述操作将$5$转换为$6$。 现在，Kevin给你一个数组$a$，它由$n$个成对不同的整数组成$(a_i\\geq2)$。 你必须找到一个整数$x\\geq2$，使得对于每个$1\\leq i\\leq n$，$x$是$a_i$的一个生成器，或者确定这样的整数不存在。 解题思路 $2$可以是任意一个非素数的生成器，对于偶数，这点显而易见。对于奇数，一个不是素数的奇数$x$总有一个除$1$以外的奇数因数，称之为$f(x)$，显然$x-f(x)$是一个偶数，可以由$2$生成，而$f(x)$也是$x-f(x)$的一个因数，故$2$可以是任意一个非素数奇数的生成器。\n对于素数而言，它的生成器只能是它本身。设$z$是一个素数，如果它有除了它本身以外的生成器，那么一定存在$m$使得$m+f(m)=z$，显然，这与$z$是一个素数矛盾。所以，当数组中含有两个及以上的素数时，该例无解。\n对于只含有一个素数的数组，该例如果有解，解一定是该素数。素数显然不能生成小于自己的、或者大小介于自己的$1-2$倍之间的数：因为此时它只有它本身一个不为$1$的因数。但是当要生成的数大于它的两倍时，情况不同。我们设这个素数为$z$，对于大于$2z$的数，我们可以先生成$2z$，获得一个因数:$2$后，就可以生成其他数了。所以，素数可以生成大于等于自己两倍的偶数。对于奇数，设这个奇数为$x$，那么如果$x-f(x)≥2z$，那么这个奇数也是可以由$z$生成的。\n所以，我们要做的事情是：先筛出数据范围内的所有素数，在读入数据时，分三种情况讨论:\n1.素数的数量大于等于二，可以直接输出$-1$。\n2.素数的数量为零，可以直接输出$2$。\n3.素数的数量为一，那么我们就要用上面的方法对数组中的每一个数据进行检查，如果每一个数据都能被这个素数生成，那么输出这个素数，否则输出$-1$。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 #include\u0026lt;iostream\u0026gt; #define ll long long using namespace std; const int MaxN=4e5+1; int t,n; int a[100001]; int Prime[MaxN]; int isComposite[MaxN]; int cnt=1; void LinearSieve(){ for(int i=2;i\u0026lt;MaxN;i++) { if(!isComposite[i]) Prime[cnt++]=i; for(int j=1;j\u0026lt;cnt\u0026amp;\u0026amp;i*Prime[j]\u0026lt;MaxN;j++) { isComposite[i*Prime[j]]=Prime[j]; if(i%Prime[j]==0) break; } } } int main(){ LinearSieve(); cin\u0026gt;\u0026gt;t; while(t--) { cin\u0026gt;\u0026gt;n; int PriCnt=0,ThePri=0,Bre=1; for(int i=1;i\u0026lt;=n;i++) { cin\u0026gt;\u0026gt;a[i]; if(!isComposite[a[i]]) { PriCnt++; ThePri=a[i]; } } if(PriCnt\u0026gt;=2) cout\u0026lt;\u0026lt;-1\u0026lt;\u0026lt;endl; else if(PriCnt==0) cout\u0026lt;\u0026lt;2\u0026lt;\u0026lt;endl; else { for(int i=1;i\u0026lt;=n;i++) { if(a[i]==ThePri) continue; if(a[i]%2==0) { if(a[i]\u0026lt;2*ThePri) { cout\u0026lt;\u0026lt;-1\u0026lt;\u0026lt;endl; Bre=0; break; } } else { if(a[i]-isComposite[a[i]]\u0026lt;2*ThePri) { cout\u0026lt;\u0026lt;-1\u0026lt;\u0026lt;endl; Bre=0; break; } } } if(Bre) cout\u0026lt;\u0026lt;ThePri\u0026lt;\u0026lt;endl; } } return 0; } 学习总结 本专题内容主要为数论，通过学习，提高了自己的能力。\n","date":"2025-02-10T00:00:00Z","permalink":"https://DustLi-DC.github.io/p/topic-four/","title":"专题四 数论"},{"content":"专题三 贪心、倍增 102400226 石华波\nVjudge账号:DustLi\n问题一\tPriority Queue 问题陈述 优先队列是一种数据结构，它维护一组元素 SS，每个元素都有一个关联的值（键），并支持以下操作：\ninsert(S,k)insert(S,k)：将一个元素 kk 插入到集合 SS 中 extractMax(S)extractMax(S)：移除并返回具有最大键的 SS 元素 编写一个程序，对优先队列 SS 执行 insert(S,k)insert(S,k) 和 extractMax(S)extractMax(S) 操作。 优先队列管理一组整数，这些整数也是优先级的键。\n输入 多个对优先队列 SS 的操作被给出。每个操作在一行中给出，格式为 \u0026ldquo;insert kk\u0026quot;、\u0026ldquo;extract\u0026rdquo; 或 \u0026ldquo;end\u0026rdquo;。这里，kk 表示要插入优先队列的整数元素。\n输入以 \u0026ldquo;end\u0026rdquo; 操作结束。\n输出 对于每个 \u0026ldquo;extract\u0026rdquo; 操作，打印从优先队列 SS 中提取的元素，每个元素占一行。\n解题思路 优先队列，套版。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include\u0026lt;iostream\u0026gt; #include\u0026lt;climits\u0026gt; #include \u0026lt;queue\u0026gt; #define ll long long using namespace std; int main() { priority_queue\u0026lt;int\u0026gt; pq; string s; cin\u0026gt;\u0026gt;s; while(s!=\u0026#34;end\u0026#34;) { if(s==\u0026#34;insert\u0026#34;) { int x; cin\u0026gt;\u0026gt;x; pq.push(x); } else if(s==\u0026#34;extract\u0026#34;) { cout\u0026lt;\u0026lt;pq.top()\u0026lt;\u0026lt;endl; pq.pop(); } cin\u0026gt;\u0026gt;s; } return 0; } 问题二\tST 表 \u0026amp;\u0026amp; RMQ 问题 问题陈述 给定一个长度为 N的数列，和 M次询问，求出每一次询问的区间内数字的最大值。\n解题思路 使用ST表，倍增地求出[i,i+2^j]区间中的最大值，输出时查表即可。\n本题读入和输出处理比较重要，如若不对cout进行处理或更换必定超时，故更换为printf并使用快读。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include \u0026lt;cmath\u0026gt; #include\u0026lt;iostream\u0026gt; #define ll long long using namespace std; int ST[20][100001]; int LogTwo[100001]; inline int read() { int x=0,f=1;char ch=getchar(); while (ch\u0026lt;\u0026#39;0\u0026#39;||ch\u0026gt;\u0026#39;9\u0026#39;){if (ch==\u0026#39;-\u0026#39;) f=-1;ch=getchar();} while (ch\u0026gt;=\u0026#39;0\u0026#39;\u0026amp;\u0026amp;ch\u0026lt;=\u0026#39;9\u0026#39;){x=x*10+ch-48;ch=getchar();} return x*f; } int main() { int N,M; N=read(); M=read(); for (int i=1;i\u0026lt;=N;i++) LogTwo[i]=log2(i); for(int i=1;i\u0026lt;=N;i++) cin\u0026gt;\u0026gt;ST[0][i]; int len=log2(N); for(int i=1;i\u0026lt;=len;i++) { for(int j=1;j\u0026lt;=N-(1\u0026lt;\u0026lt;i)+1;j++) { ST[i][j]=max(ST[i-1][j],ST[i-1][j+(1\u0026lt;\u0026lt;i-1)]); } } for(int i=1;i\u0026lt;=M;i++) { int l,r; l=read(); r=read(); len=LogTwo[r-l+1]; printf(\u0026#34;%d\\n\u0026#34;,max(ST[len][l],ST[len][r-(1\u0026lt;\u0026lt;len)+1])); } return 0; } 问题三\t合并果子 问题陈述 在一个果园里，多多已经将所有的果子打了下来，而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。\n每一次合并，多多可以把两堆果子合并到一起，消耗的体力等于两堆果子的重量之和。可以看出，所有的果子经过 n−1次合并之后， 就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。\n因为还要花大力气把这些果子搬回家，所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为 1，并且已知果子的种类 数和每种果子的数目，你的任务是设计出合并的次序方案，使多多耗费的体力最少，并输出这个最小的体力耗费值。\n例如有 3种果子，数目依次为 1 ， 2 ， 9。可以先将 1、 2堆合并，新堆数目为 3 ，耗费体力为 3 。接着，将新堆与原先的第三堆合并，又得到新的堆，数目为 12 ，耗费体力为 12 。所以多多总共耗费体力 3+12=15 。可以证明 15 为最小的体力耗费值。\n解题思路 总共需要合并的次数是已知的，即x-1次合并可以使x堆果子合并为一堆。在固定合并次数的条件下，优先合并重量较小的堆会使总消耗最少。使用小根堆，每次合并堆顶的两个元素并删除，将新生成的元素加入堆中，直到堆中仅有一个元素。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include\u0026lt;iostream\u0026gt; #include \u0026lt;queue\u0026gt; #define ll long long using namespace std; priority_queue\u0026lt;int, vector\u0026lt;int\u0026gt;,greater\u0026lt;int\u0026gt; \u0026gt; pq; int main() { int n; cin\u0026gt;\u0026gt;n; for(int i=0;i\u0026lt;n;i++) { int t; cin\u0026gt;\u0026gt;t; pq.push(t); } int sum=0; while(pq.size()\u0026gt;=2) { int a=pq.top(); pq.pop(); int b=pq.top(); pq.pop(); pq.push(a+b); sum+=a+b; } cout\u0026lt;\u0026lt;sum; return 0; } 问题4\t约瑟夫问题 问题陈述 n 个人围成一圈，从第一个人开始报数,数到 m的人出列，再由下一个人重新从 1开始报数，数到 m的人再出圈，依次类推，直到所有的人都出圈，请输出依次出圈人的编号。\n解题思路 使用队列对游戏进行模拟，每次报数增加都令此时的队首移动至队尾，当报至M时，此时的队首出列，重置计数器。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include\u0026lt;iostream\u0026gt; #include \u0026lt;queue\u0026gt; #define ll long long using namespace std; queue\u0026lt;int\u0026gt; q; int main() { int N,M; cin\u0026gt;\u0026gt;N\u0026gt;\u0026gt;M; for(int i=1;i\u0026lt;=N;i++) q.push(i); int cnt=0; while(!q.empty()) { cnt++; if(cnt==M) { cout\u0026lt;\u0026lt;q.front()\u0026lt;\u0026lt;\u0026#34; \u0026#34;; q.pop(); cnt=0; } else { int front=q.front(); q.pop(); q.push(front); } } return 0; } 问题五\tLook Up S 问题陈述 约翰的 N(1≤N≤105)头奶牛站成一排，奶牛 i的身高是 Hi(1≤Hi≤106)。现在，每只奶牛都在向右看齐。对于奶牛 i，如果奶牛 j满足 i\u0026lt;j且 Hi\u0026lt;Hj，我们可以说奶牛 i可以仰望奶牛 j。 求出每只奶牛离她最近的仰望对象。\n解题思路 使用单调栈，栈中存储奶牛序号。优先处理最右端奶牛，因为其必定没有仰望对象，使其入栈。从右向左遍历奶牛，对于每一次遍历，令身高小于此奶牛的编号出栈，若栈中仍有元素，则栈顶为其仰望对象，若栈中没有元素，则其没有仰望对象，记为0。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include\u0026lt;iostream\u0026gt; #include \u0026lt;stack\u0026gt; #define ll long long using namespace std; stack\u0026lt;int\u0026gt; s; int H[100001],F[100001]; int main() { int N; cin\u0026gt;\u0026gt;N; for(int i=1;i\u0026lt;=N;i++) cin\u0026gt;\u0026gt;H[i]; for(int i=N;i\u0026gt;=1;i--) { while(!s.empty()\u0026amp;\u0026amp;H[s.top()]\u0026lt;=H[i]) s.pop(); if(s.empty()) F[i]=0; else F[i]=s.top(); s.push(i); } for(int i=1;i\u0026lt;=N;i++) cout\u0026lt;\u0026lt;F[i]\u0026lt;\u0026lt;endl; return 0; } 问题六\t国旗计划 A 国正在开展一项伟大的计划 —— 国旗计划。这项计划的内容是边防战士手举国旗环绕边境线奔袭一圈。这项计划需要多名边防战士以接力的形式共同完成，为此，国土安全局已经挑选了 N名优秀的边防战士作为这项计划的候选人。\nA 国幅员辽阔，边境线上设有 M个边防站，顺时针编号 1至 M。每名边防战士常驻两个边防站，并且善于在这两个边防站之间长途奔袭，我们称这两个边防站之间的路程是这个边防战士的奔袭区间。N名边防战士都是精心挑选的，身体素质极佳，所以每名边防战士的奔袭区间都不会被其他边防战士的奔袭区间所包含。\n现在，国土安全局局长希望知道，至少需要多少名边防战士，才能使得他们的奔袭区间覆盖全部的边境线，从而顺利地完成国旗计划。不仅如此，安全局局长还希望知道更详细的信息：对于每一名边防战士，在他必须参加国旗计划的前提下，至少需要多少名边防战士才能覆盖全部边境线，从而顺利地完成国旗计划。\n解题思路 每位战士都不被包含，则以左端点排序，对于一个战士，其右端点左侧最近的左端点对应的战士即为能使参与人数最少的战士。\n以倍增的思路，找出每位战士下2^i个能使参与人数最少的战士，转移方程： ST[i][j]=ST[i-1][ST[i-1][j]]\n注意排序后战士编号为乱序，需存储战士读入时的编号。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 #include \u0026lt;algorithm\u0026gt; #include\u0026lt;iostream\u0026gt; #include \u0026lt;bits/ranges_algo.h\u0026gt; #define ll long long using namespace std; typedef struct{ int No; int L; int R; }Solider; const int maxN=200005; int ST[20][maxN\u0026lt;\u0026lt;1]; int Ans[maxN]; Solider Sol[maxN\u0026lt;\u0026lt;1]; int Cmp(Solider a,Solider b) { return a.L\u0026lt;b.L; } int main() { int N,M; cin\u0026gt;\u0026gt;N\u0026gt;\u0026gt;M; for(int i=1;i\u0026lt;=N;i++) { cin\u0026gt;\u0026gt;Sol[i].L\u0026gt;\u0026gt;Sol[i].R; Sol[i].No=i; Sol[i].R=Sol[i].L\u0026gt;Sol[i].R?Sol[i].R+M:Sol[i].R; } sort(Sol+1,Sol+N+1,Cmp); for(int i=1;i\u0026lt;=N;i++) { Sol[i+N].L=Sol[i].L+M; Sol[i+N].R=Sol[i].R+M; } for(int i=1,j=1;i\u0026lt;=N\u0026lt;\u0026lt;1;i++) { while(j\u0026lt;=N\u0026lt;\u0026lt;1\u0026amp;\u0026amp;Sol[i].R\u0026gt;=Sol[j].L) j++; ST[0][i]=j-1; } for(int i=1;i\u0026lt;20;i++) { for(int j=1;j\u0026lt;=N\u0026lt;\u0026lt;1;j++) { ST[i][j]=ST[i-1][ST[i-1][j]]; } } for(int i=1;i\u0026lt;=N;i++) { int ansi=0,p=i; for(int j=19;j\u0026gt;=0;j--) { if(ST[j][p]\u0026amp;\u0026amp;Sol[ST[j][p]].R\u0026lt;Sol[i].L+M) { ansi+=1\u0026lt;\u0026lt;j; p=ST[j][p]; } } Ans[Sol[i].No]=ansi+2; } for(int i=1;i\u0026lt;=N;i++) { cout\u0026lt;\u0026lt;Ans[i]\u0026lt;\u0026lt;\u0026#34; \u0026#34;; } return 0; } 学习总结 本专题内容主要为贪心与倍增，通过学习，提高了自己的能力。\n","date":"2025-02-07T00:00:00Z","permalink":"https://DustLi-DC.github.io/p/topic-three/","title":"专题三 贪心、倍增"},{"content":"专题二 二分法 102400226 石华波\tVjudge账号:DustLi\n问题一\t二分查找 问题陈述 输入一个整数 n和 n个整数，保证这 n个整数已经按照从小到大进行排序。\n然后输入一个整数 q（ q≤100000 ）代表 q次查询。接下来 q行，每行含有一个整数 m，代表一次查询。对于每次查询，使用二分查找判断 m是否在之前输入的 n个整数中出现过。如果出现，输出一行 \u0026ldquo;Yes\u0026rdquo;，否则输出 \u0026ldquo;No 。\n解题思路 基本的二分查找，直接套板即可。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include\u0026lt;iostream\u0026gt; using namespace std; int main(){ int n; cin\u0026gt;\u0026gt;n; int array[n]; for(int i=0;i\u0026lt;n;i++) { cin\u0026gt;\u0026gt;array[i]; } int q; cin\u0026gt;\u0026gt;q; while(q--) { int m; cin\u0026gt;\u0026gt;m; int beg=0,end=n-1,mid=0; while(beg\u0026lt;=end) { mid=(beg+end)/2; if(array[mid]==m) break; if(array[mid]\u0026lt;m) beg=mid+1; else end=mid-1; } if(array[mid]==m) cout\u0026lt;\u0026lt;\u0026#34;YES\u0026#34;\u0026lt;\u0026lt;endl; else cout\u0026lt;\u0026lt;\u0026#34;NO\u0026#34;\u0026lt;\u0026lt;endl; } return 0; } 问题二\tA-B数对 问题陈述 给出一串正整数数列以及一个正整数 C，要求计算出所有满足 A−B=C的数对的个数（不同位置的数字一样的数对算不同的数对）。\n解题思路 解题时并未联系到二分查找，而是使用Hash表，遍历数组，当HASH表中存在A=B+C时，答案加上A出现的次数。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include\u0026lt;iostream\u0026gt; #include\u0026lt;unordered_map\u0026gt; using namespace std; int main(){ int N,C; int tag; long long ans=0; cin\u0026gt;\u0026gt;N\u0026gt;\u0026gt;C; int nums[N]; unordered_map\u0026lt;int,int\u0026gt; numcnt; for(int i=0;i\u0026lt;N;i++) { cin\u0026gt;\u0026gt;nums[i]; numcnt[nums[i]]++; } for(int i=0;i\u0026lt;N;i++) { tag=nums[i]+C; ans+=numcnt[tag]; } cout\u0026lt;\u0026lt;ans\u0026lt;\u0026lt;endl; return 0; } 问题三\t分巧克力 问题陈述 儿童节那天有 K 位小朋友到小明家做客。小明拿出了珍藏的巧克力招待小朋友们。\n小明一共有 NN 块巧克力，其中第 i 块是 Hi×Wi的方格组成的长方形。\n为了公平起见，小明需要从这 N 块巧克力中切出 K 块巧克力分给小朋友们。切出的巧克力需要满足：\n形状是正方形，边长是整数。 大小相同。 例如一块 6×5的巧克力可以切出 6 块 2×2的巧克力或者 2块 3×3的巧克力。\n当然小朋友们都希望得到的巧克力尽可能大，你能帮小明计算出最大的边长是多少么？\n解题思路 题目要求每块巧克力大小相同，而对于一块H[i]*W[i]的巧克力，最多能切出边长为i的巧克力(H[i]/i)*(W[i]/i)份。\n使用二分法，由题意可知最大边长不会超过已知Hi、Wi最大值中较小的那个，故以该值为上界，1为下界，当可切出的块数大于等于K时更新下界，否则更新上界。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include\u0026lt;iostream\u0026gt; #include\u0026lt;climits\u0026gt; using namespace std; int main(){ int N,K; cin\u0026gt;\u0026gt;N\u0026gt;\u0026gt;K; int H[N],W[N]; int Max=INT_MIN; for(int i=0;i\u0026lt;N;i++) { cin\u0026gt;\u0026gt;H[i]\u0026gt;\u0026gt;W[i]; Max=max(Max,min(H[i],W[i])); } int Min=1; int mid; int cnt; while(Min\u0026lt;Max) { mid=(Min+Max+1)/2; cnt=0; for(int i=0;i\u0026lt;N;i++) { cnt+=(H[i]/mid)*(W[i]/mid); } if(cnt\u0026gt;=K) Min=mid; else Max=mid-1; } cout\u0026lt;\u0026lt;Min\u0026lt;\u0026lt;endl; return 0; } 问题4\t卡牌 问题陈述 这天，小明在整理他的卡牌。\n他一共有 n 种卡牌，第 ii 种卡牌上印有正整数数 i(i∈[1,n]), 且第 i种卡牌现有 ai张。\n而如果有 n 张卡牌，其中每种卡牌各一张，那么这 n 张卡牌可以被称为一套牌。小明为了凑出尽可能多套牌，拿出了 m 张空白牌, 他可以在上面写上数 i，将其当做第 i 种牌来凑出套牌。然而小明觉得手写的牌不太美观，决定第 i种牌最多手写 bi张。\n请问小明最多能凑出多少套牌?\n解题思路 首先，十年OI一场空，不开LongLong见祖宗！\n同样是用二分法查找最多能凑出的牌数，此处上界可以是max(Right,b[i]+a[i])，但对结果影响不大，故没有进行修改，使用了与上一题不同的二分板子。\n以1为下界，一个足够大的数1e7为上界，Check函数用于检查是否能够凑出mid套牌，如果mid-a[i]\u0026gt;b[i]，说明无论如何此种牌都无法凑到mid张，可直接return 0,循环结束后将sum与m比较，如若sum小于等于m则说明可以凑出mid套牌，反之不然。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include\u0026lt;iostream\u0026gt; #include\u0026lt;climits\u0026gt; #define ll long long using namespace std; const ll N=2e5+10; ll n,m; ll a[N],b[N]; ll Check(ll x); int main() { cin\u0026gt;\u0026gt;n\u0026gt;\u0026gt;m; for(ll i=0;i\u0026lt;n;i++) cin\u0026gt;\u0026gt;a[i]; for(ll i=0;i\u0026lt;n;i++) cin\u0026gt;\u0026gt;b[i]; ll Left=0,Right=1e7; while(Left\u0026lt;Right) { ll mid=(Right+Left+1)/2; if(Check(mid)) Left=mid; else Right=mid-1; } cout\u0026lt;\u0026lt;Right\u0026lt;\u0026lt;endl; return 0; } ll Check(ll x) { ll sum=0; for(ll i=0;i\u0026lt;n;i++) { if(x-a[i]\u0026gt;b[i]) return 0; sum+=x-a[i]\u0026gt;0?x-a[i]:0; } return sum\u0026lt;=m; } 问题五\t书的复制 问题陈述 现在要把 m 本有顺序的书分给 k个人复制（抄写），每一个人的抄写速度都一样，一本书不允许给两个（或以上）的人抄写，分给每一个人的书，必须是连续的，比如不能把第一、第三、第四本书给同一个人抄写。\n现在请你设计一种方案，使得复制时间最短。复制时间为抄写页数最多的人用去的时间。\n解题思路 二分法此处不表，与上文类似。\n复制时间即抄写页数最多的人所抄页数，该值最大为所有书页码之和，最小为页码数最多那本书的页码数，以此为上下界。\nCheck函数中，检查抄的页数加上当前书的页数是否超过mid（所求复制时间），是则换人，否则继续，最后比较所需人数与实际人数，若所需人数小于等于实际人数则可行，反之不然。\n在输出答案时，选择从后往前遍历，即让后面的人多抄以达到前面的人少抄的效果，记录下每个人的结束书号，第一个人的起始书号为1，最后一个人的结束书号为n，当前人的起始书号为上一个人的结束书号+1。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 #include\u0026lt;iostream\u0026gt; #include\u0026lt;climits\u0026gt; #define ll long long using namespace std; int Check(int upp); int book,people; int books[502]; int main() { cin\u0026gt;\u0026gt;book\u0026gt;\u0026gt;people; int left=0,right=0; for(int i=1;i\u0026lt;=book;i++) { cin\u0026gt;\u0026gt;books[i]; right+=books[i]; left=max(left,books[i]); } int mid=0; while(left+1\u0026lt;right) { mid=(left+right)/2; if(Check(mid)) right=mid; else left=mid; } int last[505],cnt=1; last[1]=book+1; last[people+1]=1; int pages=0; for(int i=book;i\u0026gt;0;i--) { if(pages+books[i]\u0026gt;right) { pages=0; last[++cnt]=i+1; } pages+=books[i]; } for(int i=people;i\u0026gt;0;i--) { cout\u0026lt;\u0026lt;last[i+1]\u0026lt;\u0026lt;\u0026#34; \u0026#34;\u0026lt;\u0026lt;last[i]-1\u0026lt;\u0026lt;endl; } return 0; } int Check(int upp) { int pcnt=1,pages=0; for(int i=book;i\u0026gt;0;i--) { if(pages+books[i]\u0026gt;upp) pages=0,pcnt++; pages+=books[i]; } return pcnt\u0026lt;=people; } 问题六\t青蛙过河 小青蛙住在一条河边，它想到河对岸的学校去学习。小青蛙打算经过河里的石头跳到对岸。\n河里的石头排成了一条直线，小青蛙每次跳跃必须落在一块石头或者岸上。不过，每块石头有一个高度，每次小青蛙从一块石头起跳，这块石头的高度就会下降 1，当石头的高度下降到 0时小青蛙不能再跳到这块石头上（某次跳跃后使石头高度下降到 0是允许的)。\n小青蛙一共需要去学校上 x天课，所以它需要往返 2x次。当小青蛙具有一个跳跃能力 y时，它能跳不超过 y的距离。\n请问小青蛙的跳跃能力至少是多少才能用这些石头上完 x次课。\n解题思路 一只青蛙往返2x次\u0026lt;=\u0026gt;一只青蛙单向过河2x次\u0026lt;=\u0026gt;2x只青蛙单向过河1次。\n在第i次跳越中，这些青蛙必将落在[i,y+i-1]的区间中，故每个这样的区间中石头高度之和必须大于等于2x，此为必要性。\n接下来说明充分性：当青蛙们在[i,y+i-1]的区间内上落脚时，令落在石头i上的青蛙向前跳跃[1,y]格，则此时所有的青蛙都处于[i+1,y+1]区间中，由于每个区间的石头高度之和均大于等于2x，所以青蛙均能顺利落脚。显然，这2x只青蛙可以在[1,y]上落脚，那么以此类推，他们就可以在[2,y+1]、[3,y+2]……上落脚，故所有青蛙均能过河。\n但如果每次都以H[i]+\u0026hellip;+H[y+i-1]的方式计算区间高度和，用时就太久了，故计算从石头1到石头n的高度和Sum[i]，每次只需令区间端点的高度和相减即可。\nAC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include\u0026lt;iostream\u0026gt; #include\u0026lt;climits\u0026gt; #define ll long long using namespace std; ll Check(ll y); const ll N=1e5+1; ll width,days; ll H[N],Sum[N]={0}; int main() { cin\u0026gt;\u0026gt;width\u0026gt;\u0026gt;days; days*=2; for(ll i=1;i\u0026lt;width;i++) { cin\u0026gt;\u0026gt;H[i]; Sum[i]=Sum[i-1]+H[i]; } ll left=1,right=width; while(left\u0026lt;right) { ll mid=(left+right)/2; if(Check(mid)) right=mid; else left=mid+1; } cout\u0026lt;\u0026lt;left\u0026lt;\u0026lt;endl; return 0; } ll Check(ll y) { for(ll i=y;i\u0026lt;width;i++) { if(Sum[i]-Sum[i-y]\u0026lt;days) return 0; } return 1; } 学习总结 本专题内容主要为二分法的应用，通过学习，解决了两道绿题，提高了自己的能力。\n","date":"2025-01-26T00:00:00Z","permalink":"https://DustLi-DC.github.io/p/topic-two/","title":"专题二 二分法"},{"content":"专题一 学习C++ 102400226 石华波 Vjudge账号:DustLi\n问题一\tLong 问题陈述 对于一个正整数X，级别为X 的 龙字符串 是一个长度为 (X+3)的字符串，由一个 L、X 次出现的 o、一个 n 和一个 g 按此顺序排列而成。\n给定一个正整数N。打印级别为N 的龙字符串。 注意，大写字母和小写字母是有区别的。\n约束条件 1≤N≤20241≤N≤2024 N是一个整数。 解题思路 读取整数N后，输出L，再通过循环输出N个o，最后输出n、g即可，AC代码：\n1 2 3 4 5 6 7 8 9 10 11 #include \u0026lt;iostream\u0026gt; using namespace std; int main() { int N; cin\u0026gt;\u0026gt;N; cout\u0026lt;\u0026lt;\u0026#39;L\u0026#39;; while(N--) cout\u0026lt;\u0026lt;\u0026#39;o\u0026#39;; cout\u0026lt;\u0026lt;\u0026#34;ng\u0026#34;\u0026lt;\u0026lt;endl; return 0; } 问题二\tYES or YES？ 问题陈述 有一个长度为 3 的字符串 s，由大写和小写英文字母组成。检查它是否等于 \u0026ldquo;YES\u0026rdquo;（不带引号），其中每个字母可以是任意大小写。例如，\u0026ldquo;yES\u0026rdquo;、\u0026ldquo;Yes\u0026rdquo;、\u0026ldquo;yes\u0026rdquo; 都是允许的。\n解题思路 逐个判断字符是否为\u0026rsquo;y\u0026rsquo;、\u0026lsquo;Y\u0026rsquo;……即可，AC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include \u0026lt;iostream\u0026gt; #include \u0026lt;string\u0026gt; using namespace std; int main() { int N; cin\u0026gt;\u0026gt;N; while(N--) { string s; cin\u0026gt;\u0026gt;s; if(s[0]!=\u0026#39;y\u0026#39;\u0026amp;\u0026amp;s[0]!=\u0026#39;Y\u0026#39;) cout\u0026lt;\u0026lt;\u0026#34;NO\u0026#34;\u0026lt;\u0026lt;endl; else if(s[1]!=\u0026#39;e\u0026#39;\u0026amp;\u0026amp;s[1]!=\u0026#39;E\u0026#39;) cout\u0026lt;\u0026lt;\u0026#34;NO\u0026#34;\u0026lt;\u0026lt;endl; else if(s[2]!=\u0026#39;s\u0026#39;\u0026amp;\u0026amp;s[2]!=\u0026#39;S\u0026#39;) cout\u0026lt;\u0026lt;\u0026#34;NO\u0026#34;\u0026lt;\u0026lt;endl; else cout\u0026lt;\u0026lt;\u0026#34;YES\u0026#34;\u0026lt;\u0026lt;endl; } return 0; } 问题三\tEven? Odd? G 问题陈述 Bessie那惨无人道的二年级老师搞了一个有 N 个正整数 I 的表叫Bessie去判断“奇偶性”（这个词语意思向二年级的学生解释，就是“这个数是单数，还是双数啊？”）。Bessie被那个表的长度深深地震惊到了，竟然跟栋栋的泛做表格一样多道题！！！毕竟她才刚刚学会数数啊。\n写一个程序读入N个整数，如果是双数，那么在单立的一行内输出\u0026quot;even\u0026quot;，如果是单数则类似地输出\u0026quot;odd\u0026quot;.\n数据范围：每个正整数不超过 10^60\n解题思路 数据范围远超int 或者long long能表示的范围，但题目仅要求判断奇偶，故以string的方式读入，判断最后一位的奇偶即可。幸运的是，数字的ASCII码的奇偶性与其本身一致，故不需要额外进行处理，AC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include \u0026lt;iostream\u0026gt; #include \u0026lt;string\u0026gt; using namespace std; int main() { int N; cin\u0026gt;\u0026gt;N; while(N--) { string s; cin\u0026gt;\u0026gt;s; if(s[s.length()-1]%2) cout\u0026lt;\u0026lt;\u0026#34;odd\u0026#34;\u0026lt;\u0026lt;endl; else cout\u0026lt;\u0026lt;\u0026#34;even\u0026#34;\u0026lt;\u0026lt;endl; } return 0; } 问题4\tProblem Generator 问题陈述 Vlad计划下个月举办m轮比赛。每一轮比赛应包含难度等级为\u0026rsquo;A\u0026rsquo;, \u0026lsquo;B\u0026rsquo;, \u0026lsquo;C\u0026rsquo;, \u0026lsquo;D\u0026rsquo;, \u0026lsquo;E\u0026rsquo;, \u0026lsquo;F\u0026rsquo;, 和 \u0026lsquo;G\u0026rsquo; 的一个问题。\nVlad已经准备了n个问题，其中第i个问题的难度等级为a(i)。可能这些问题数量不够，所以他可能需要再想出一些问题。\nVlad希望尽可能少地想出问题，因此他请你找出他需要想出的问题的最少数量，以便举办m轮比赛。\n例如，如果m=1，n=10，a= \u0026lsquo;BGECDCBDED\u0026rsquo;，那么他需要想出两个问题：一个难度等级为\u0026rsquo;A\u0026rsquo;，一个难度等级为\u0026rsquo;F\u0026rsquo;。\n解题思路 依题意，每种问题需要m个，故读入m、n，计算每种问题已有数量和总共所需之差再求和即可（缺少的问题\u0026lt;0的不计），AC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include \u0026lt;iostream\u0026gt; using namespace std; int main() { int t; cin\u0026gt;\u0026gt;t; while(t--) { int n; int m; char c; cin\u0026gt;\u0026gt;n\u0026gt;\u0026gt;m; int cnt[]={m,m,m,m,m,m,m}; int ans=0; while(n--) { cin\u0026gt;\u0026gt;c; cnt[c-65]--; } for(int i=0;i\u0026lt;7;i++) if(cnt[i]\u0026gt;0) ans+=cnt[i]; cout\u0026lt;\u0026lt;ans\u0026lt;\u0026lt;endl; } return 0; } 问题五\trules 问题陈述 小 A 制定了一些规则，每条规则有一个代号，代号为不超过 10^9的非负整数。\n小 A 的国家有 n位居民，每位居民每天会且仅会遵守 1条规则。小 A 记录了 m天里每天每位居民遵守的规则代号。\n现在小 A 想要考察代号为 k的规则是否符合民意，具体考察方法如下：\n如果在某一天里，有大于等于一半的人遵守了规则 k，那么小 A 认为在这一天规则 k是符合民意的。 如果在大于等于一半的天数里，规则 k符合民意，那么他会认为规则 k是正确的。否则，他会认为规则 k是错误的。 如果小 A 的规则 k是正确的，请你输出 YES，否则请你输出 NO。\n解题思路 分别计算每一天遵循该规则的居民数量是否大于等于总数的一半，最后再计算符合民意的天数是否大于等于m的一半即可（实现方法使用的是将遵循规则的居民数量、符合民意的天数乘以2与总居民数、m进行比较，不需要引入float类型，乘法比除法更快），AC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include \u0026lt;iostream\u0026gt; using namespace std; int main() { int n,m,k; int cntt=0; cin\u0026gt;\u0026gt;n\u0026gt;\u0026gt;m\u0026gt;\u0026gt;k; for(int z=0;z\u0026lt;m;z++) { int cntd=0; for(int i=0;i\u0026lt;n;i++) { int d; cin\u0026gt;\u0026gt;d; cntd += d==k; } cntt += 2*cntd\u0026gt;=n; } if(2*cntt\u0026gt;=m) cout\u0026lt;\u0026lt;\u0026#34;YES\u0026#34;\u0026lt;\u0026lt;endl; else cout\u0026lt;\u0026lt;\u0026#34;NO\u0026#34;\u0026lt;\u0026lt;endl; return 0; } 问题六\tMany Replacement 问题陈述 给定一个长度为 N的字符串 S，由小写英文字母组成。\n你将对字符串 S 执行 Q 次操作。 第 i次操作 (1≤i≤Q)由一对字符 (c(i),d(i)) 表示，对应以下操作：\n将字符串 S中所有字符 c(i)替换为字符 d(i)。 在所有操作完成后，打印字符串 S。\n约束条件 1≤N≤2×10^5 S是一个长度为 N的字符串，由小写英文字母组成。 1≤Q≤2×10^5 c(i)和 d(i)是小写英文字母 (1≤i≤Q) N和 Q是整数。 解题思路 在字符串长度较长的情况下，每次都遍历字符串进行修改是不现实的（很有可能会TLE）。发现经过多次交换后，每个字母对应映射到另一字母，那么维护一个字母表（初始映射到自身），每次对字母表的映射进行修改，在最后再遍历字符串进行修改即可，AC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include \u0026lt;iostream\u0026gt; #include \u0026lt;string\u0026gt; using namespace std; int main() { int N; string s; int Q; cin\u0026gt;\u0026gt;N\u0026gt;\u0026gt;s\u0026gt;\u0026gt;Q; char Letters[26]; for(int i=0;i\u0026lt;26;i++) Letters[i]=i+\u0026#39;a\u0026#39;; while(Q--) { char c,d; cin\u0026gt;\u0026gt;c\u0026gt;\u0026gt;d; for(int i=0;i\u0026lt;26;i++) { if(Letters[i]==c) { Letters[i]=d; } } } for(int i=0;i\u0026lt;N;i++) { s[i]=Letters[s[i]-\u0026#39;a\u0026#39;]; } cout\u0026lt;\u0026lt;s\u0026lt;\u0026lt;endl; return 0; } 问题七\t更好的交换 问题陈述 小 S 有一个奇怪的机关拼图。这个拼图可以看作一个 n行 n列的方阵 A，第 i行第 j列的位置上有一个正整数 A(i,j)。\n与寻常拼图不同的是，这个机关拼图上的数字不能随意移动，必须按照如下规则之一操作：\n选择拼图上的第 x行和第 y行，交换这两行； 选择拼图上的第 x列和第 y列，交换这两列。 为了复原这个拼图，小 S 将会操作共 m次，每次操作格式如下：\n1 x y，表示交换第 x行和第 y行； 0 x y，表示交换第 x列和第 y列； 请你输出复原后的拼图。\n解题思路 发现并不需要实际地交换行和列，只需要维护两个数组Row、Col，对数组中行与列映射代表的行与列关系进行修改即可，与上一题其实类似。AC代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include \u0026lt;iostream\u0026gt; using namespace std; int Matrix[1002][1002]; int Row[1002],Col[1002]; int main() { int n,m; cin\u0026gt;\u0026gt;n\u0026gt;\u0026gt;m; for(int i=1;i\u0026lt;=n;i++) { for(int j=1;j\u0026lt;=n;j++) { cin\u0026gt;\u0026gt;Matrix[i][j]; } } for(int i=1;i\u0026lt;=n;i++) Row[i]=Col[i]=i; while(m--) { int op,x,y; cin\u0026gt;\u0026gt;op\u0026gt;\u0026gt;x\u0026gt;\u0026gt;y; if(op) swap(Row[x],Row[y]); else swap(Col[x],Col[y]); } for(int i=1;i\u0026lt;=n;i++) { for(int j=1;j\u0026lt;=n;j++) { cout\u0026lt;\u0026lt;Matrix[Row[i]][Col[j]]\u0026lt;\u0026lt;\u0026#34; \u0026#34;; } cout\u0026lt;\u0026lt;endl; } return 0; } 学习总结 本专题内容主要为C++基础语法，通过学习，对时间复杂度和空间复杂度的计算有了更深入的了解，对STL的内容有了了解。\n","date":"2025-01-23T00:00:00Z","permalink":"https://DustLi-DC.github.io/p/topic-one/","title":"专题一 学习C++"}]