BZOJ1033 | ZJOI2008 杀蚂蚁 AntBuster

<论一次杀蚂蚁的经历>

<论如何优雅地用工程代码风格写出杀蚂蚁>

引子:

前天(也就是2018-3-18)的时候,刷B站(BZOJ)突然想起要A这题……

于是怂恿WHY一起做……

然鹅种种原因,拖到昨天(2018-3-19)下午才开始做

于是在CYYZ群里开起了校车……

然后

因为巨长的变量名不想手残于是换了VSCode……

然后疯狂盖楼……

 

注意这已经是今天的中午了……,而我交了一遍得了30

下午来了遂调试……发现了几个zz错误

等级没算对啦……sort忘记+1啦……蚂蚁位置忘记更新啦……以及更为严重的,没算点积判断附加攻击到底能不能打到。

晚上开始怀疑人生……此时代码量超过10k

下载了测试数据本地测……开Lemon却不小心点了O2

结果最后半个小时在静态查错,然而并没有什么错,后来的手动测试也证明了这一点

是Lemon开了O2之后,把程序跑错了

我果然花了几乎1h试图卡掉通过开了O2的Lemon……(滑稽)

管他的,进入正题

 

 

 

 


继续阅读“BZOJ1033 | ZJOI2008 杀蚂蚁 AntBuster”

树链刨分(启发式剖分)

的梗来自于Yveh在SDWC2018上的课件

 

线段树背今晚的大锅

在这里再说一点轶事,据说集训队大爷WZD神犇出过一道带八个操作的LCT

Exciting.

还有我成功拿到了4k的长度,代码更加高清了。

//牺牲一些长度换易读性吧 反正手速快。[滑稽

没错这就是洛谷的板子题.

#洛谷 P1860 新魔法药水

神奇的DP

大概是用DP预处理要预处理的内容

然后用DP预处理

最后DP背包跑答案

 

卡了我很久有必要详细讲一下

题目描述

商店里有N种药水,每种药水都有一个售价和回收价。小S攒了V元钱,还会M种魔法,可以把一些药水合成另一种药水。他一天可以使用K次魔法,问他一天最多赚多少钱?

输入输出格式

输入格式:

第一行四个数N、M、V、K

接下来N行,每行两个数,表示药水的售价和回收价。

接下来M行,每行若干个数,第一个数表示魔法的成品,第二个数是原料的种数,接下来为各种原料的编号

输出格式:

一个数,表示小S的最大利润

·

N<=60 M<=240

V<=1000

k<=30

我们先考虑这是个背包

然后很自然的只需要求每个物品的min 购买价,然后二位费用xjb转移就可以得出答案

DP求这个部分.

因为有魔法,所以设计状态dx[i][j]表示物品i在当前用了j次魔法的情况下最小的购买价,这样才能当做背包来DP

dx怎么求呢?也不好求,所以这就是卡我的地方,我一开始还以为是DFS求

 

厚颜无耻的看题解……

你需要在枚举每个魔法w的时候都dp处理出 第w个魔法所需的前x个物品 用y次魔法 可以得到的最小购买价

设计状态ori[x][y]表示这样,每次枚举了一个魔法w都要求一次ori,注意y≤枚举的魔法次数

然后DP一波ori,这个很简单,写过几道提高水平的DP就会求

然后用ori[w需要的物品总数][枚举魔法次数-1]更新dx[w产成品][枚举的魔法次数]

·

这样我们就有了dx

xjb背包即可.XXXD

#洛谷3917 异或序列

对于100% 的数据,1≤n≤105

做法是求异或前缀和然后按位讨论贡献

如果当前位k有奇数个1那么贡献为(numof1×numof0)×(1<<k)

 

带修莫队板子

丢板子还需要别的内容吗

题目参见数颜色

JLOI2009 二叉树问题

题目描述

如下图所示的一棵二叉树的深度、宽度及结点间距离分别为:

深度:4 宽度:4(同一层最多结点个数)

结点间距离: ⑧→⑥为8 (3×2+2=8)

⑥→⑦为3 (1×2+1=3)

注:结点间距离的定义:由结点向根方向(上行方向)时的边数×2,

与由根向叶结点方向(下行方向)时的边数之和。

 

输入输出格式

输入格式:

输入文件第一行为一个整数n(1≤n≤100),表示二叉树结点个数。接下来的n-1行,表示从结点x到结点y(约定根结点为1),最后一行两个整数u、v,表示求从结点u到结点v的距离。

输出格式:

三个数,每个数占一行,依次表示给定二叉树的深度、宽度及结点u到结点v间距离。

无耻的借用洛谷的图片

这题就是个智障

首先dfs一下然后bfs一下然后lca一波就行了

一开始忘记讨论深度了然后使劲在那WA。。

//我知道不用写LCA和BFS但我就是想写你打我啊!!!!

#洛谷P2448 无尽的生命

题目描述

逝者如斯夫,不舍昼夜!

叶良辰认为,他的寿命是无限长的,而且每天都会进步。

叶良辰的生命的第一天,他有1点能力值。第二天,有2点。第n天,就有n点。也就是S[i]=i

但是调皮的小A使用时光机,告诉他第x天和第y天,就可以任意交换某两天的能力值。即S[x]<–>S[y]

小A玩啊玩,终于玩腻了。

叶良辰:小A你给我等着,我有100种办法让你生不如死。除非能在1秒钟之内告知有多少对“异常对”。也就是说,最后的能力值序列,有多少对的两天x,y,其中x<y,但是能力值S[x]>S[y]?

小A:我好怕怕啊。

于是找到了你。

对于30%的数据,x_i,y_i <= 2000

对于70%的数据, x_i,y_i <= 100000

对于100%的数据, x_i.y_i <= 2^31-1 k<=100000

逆序对裸题

由于xy这么大我们来离散化

把只要是交换过的(不管几次)都标记下来,排下序(点权为1)

没交换过的一些区间缩成一个点(点权为区间长度)

然后进行离散化操作,离散成新的段,相邻两点间下标严格递增1(还有一个就是开头没被交换的区间对答案没有影响直接扔掉)

我们离线了交换操作,这个时候就二分查找,把原来应该换哪两个点映射成现在应该换哪两个点

全换完之后就是个裸的逆序对问题,按点权建树状数组,然后统计区间和就行了。

·

//注释是做题的时候瞎写的凑合着看看吧,要说的都说了。

BZOJ2028 | SHOI2009 Booking 会场预约

题目描述

PP大厦有一间空的礼堂,可以为企业或者单位提供会议场地。这些会议中的大多数都需要连续几天的时间(个别的可能只需要一天),不过场地只有一个,所以不同的会议的时间申请不能够冲突。也就是说,前一个会议的结束日期必须在后一个会议的开始日期之前。所以,如果要接受一个新的场地预约申请,就必须拒绝掉与这个申请相冲突的预约。一般来说,如果PP大厦方面事先已经接受了一个会场预约,例如从10日到15日,就不会在接受与之相冲突的预约,例如从12日到17日。不过,有时出于经济利益,PP大厦方面有时会为了接受一个新的会场预约,而拒绝掉一个甚至几个之前预订的预约。于是,礼堂管理员QQ的笔记本上笔记本上经常记录着这样的信息: 本题中为方便起见,所有的日期都用一个整数表示。例如,如果一个为期10天的会议从“90日”开始到“99日”,那么下一个会议最早只能在“100日”开始。最近,这个业务的工作量与日俱增,礼堂的管理员QQ希望参加SHTSC的你替他设计一套计算机系统,方便他的工作。这个系统应当能执行下面两个操作: A操作:有一个新的预约是从“start日”到“end日”,并且拒绝掉所有与它相冲突的预约。执行这个操作的时候,你的系统应当返回为了这个新预约而拒绝掉的预约个数,以方便QQ与自己的记录相校对。 B操作:请你的系统返回当前的仍然有效的预约的总数。


输入格式

输入文件的第一行是一个整数n,表示你的系统将接受的操作总数。接下去n行每行表示一个操作。每一行的格式为下面两者之一: “A start end”表示一个A操作; “B”表示一个B操作。


输出格式

输出文件有n行,每行一次对应一个输入。表示你的系统对于该操作的返回值。

二分+树状数组/线段树

还是用树状数组吧。

我们考虑所有接受的预约的开始时间都是严格递增的,那么我们建立Fenwick Tree,add(i,1)表示开始时间为i的预约存在,加入fwt

然后考虑拒绝的情况

二分查找所有开始时间小于等于当前预约结束时间的预约(只有这样才有可能被拒绝)

然后找到:开始时间与当前预约的结束时间最接近的一个预约。检查它的结束时间是否大于等于当前预约的开始时间建议画区间图

考虑未加入当前预约之前所有预约的开始、结束时间都是满足严格递增关系的,如果找到的最接近结束时间的都不与当前预约冲突,那在它之前的所有预约其结束时间严格递减,更不可能被拒绝。

所以直接break

而考虑右边(时间轴),如果能找到开始时间在当前预约的结束时间左边的预约,那就不可能跑到当前预约的右面。整体在当前预约右面的也一定不会被二分找到。

所以这样考虑到了所有情况

然后就统计一下输出就行了。

注意拒绝的时候还要add(position,-1),还有就是一开始就要查一下在当前预约的结束时间左边一共有多少预约。用这个二分。

#洛谷 P1120 小木棍

题目描述

乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50。

现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。

给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。

先写一个暴力……

暴力最好写了,然后再考虑剪枝

这是暴力:记录一下当前那根大木棍已经接了多长,已经接了多少根大木棍和一根大木棍应该有多长。

当然暴力也可以加一个简单的剪枝:记录一下总长度,如果当前枚举的一根大木棍的应有长度不能整除总长度那就不搜。

然后剪枝:

1.我们按长度排序,如果当前正在拼一根新的木棍,而且最大的一根小木棍加上当前的长度(应该是0)都不能满足枚举到的当前大木棍长度那就剪掉。

2.还是上述操作,不过是剪掉:整整一根小木棍都比当前枚举到的大木棍的长度长的情况(那样显然无解)

3.还是排序之后,记录一下上次尝试了哪一根小木棍(也就是长度)如果这次枚举到的小木棍的长度和上次相等那么也剪掉。

4.如果搜的目标是拼最后一根大木棍了,而且最后还无解,回溯回来了,那这组直接放弃。

就这些?

对,一共就这么六个剪枝 (排序&暴力的剪枝&1234)

试试刚刚才意识到的高亮操作

#洛谷P1967 货车运输

题目描述

A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。

输入输出格式

输入格式:

输入文件名为 truck.in。

输入文件第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道

路。 接下来 m 行每行 3 个整数 x、 y、 z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z 的道路。注意: x 不等于 y,两座城市之间可能有多条道路 

接下来一行有一个整数 q,表示有 q 辆货车需要运货。

接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意: x 不等于 y 

输出格式:

输出文件名为 truck.out。

输出共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货

车不能到达目的地,输出-1。

先Kruskal跑一下最大生成树,就是把边权改成从大到小排序就行。

然后用倍增思想在有根树上记录一个minp[i][k]数组,表示这个点i上跳2k的高度,经过路径的最小边权:

显然它等于minp[i][k-1]和minp[l[i][k-1][k-1]中的最小值。也就是它向上跳2k-1高度的minp和从depth[i]-2k-1高度再往上跳2k-1高度的minp(参照倍增的思想)

而向上跳2k-1高度能够达到点l[i][k-1],这个就属于LCA的基本操作了吧……

注意初始化。

还要维护一下点的联通行……用Kruskal留下来的UFS就行。