题目请去洛谷上找找吧。我不复制粘贴了。
由于差不多有1年了,所以我把6道题全部都重新做了一遍。
所以题解没有看过任何网上的资料……全都是凭借当初的信息构筑起来的。
代码也按照模糊的记忆重写了(部分啦部分=v=)。。
Day1
T1:首先,看出a和b是互质的(虽然当时我并不是马上看出这一点)。
能够被支付的物品价格p满足p=ax+by,其中,x和y都是 非负 (非负!)整数。
那么由简单的数论,我们知道,p能够被表示,意思就是让我们去找出x和y这组解,
而其实,由于a,b互质,所有的p都是可以被表示的(定理),
因此问题不是出在无解,问题是出在解不合法。
刚才说过,x和y要满足非负,但是同时,注意到,x的周期是b,y的周期是a,
这句话的意思是,当p=ax+by时,x变成x+t*b,而y变成y-t*a,t是整数,那么等式仍然成立。
所以解是无穷多组,而且我们可以构造出来。
因此,解不合法,就意味着p=ax+by的无穷多组解,全部满足x,y至少有一个是负数。
考虑临界点就好了:当x,y恰好一正一负时,然后我们令t=1对x,y作变换,使得这种变换让x,y中负的那个变成正的;
矛盾的出现点,就是那个正的变成负的。
比如,x=b-1,y=-1(这个构造的思想,基于我们要让p尽量大,即x和y尽量大)
此时很显然是临界点,p=a*(b-1)+(-1)*a=a*b-a-b
又比如,x=-1,y=a-1,计算出的p显然会相同。
所以答案就是a*b-a-b,注意,爆int。。
这题贴代码没意义把??
T2:
呃。
不知道如何评价,因为就是一道很蠢萌的模拟。
好吧我承认这题我没有重写,因为真的很蠢萌。。2333
T3:
woo,当年让我吃了屎的题(之一)
考试的时候没调对,结果正常小暴力打错了……
事实证明当时确实差那么几个重要的点。
如果仔细去分析题目的话,会发现其实题目的要求是十分清晰的。
因为,所谓的“有无穷组路线”,其实就是0环的情况。
但是单单判断存在0环还不够,我们要判断出:0环是否存在于一条可能在范围内的路径上。
看上去问题复杂了许多,实则不然。
判断0环这点考场上想复杂了,当时我在纠结如何把0环缩起来……
其实如果代码能力强就可以了。。然而。。
其实直接拓扑排序就好了啊= =我tm今天早上一拍头。。woc。。。
好吧,如果想到拓扑了那么所有问题都解了。。。我当时就是tarjan写炸,dp用bfs写的也炸了。。
咳咳,回归正题。
判断0环可以重新构建一张图,只保留0边,那么再用拓扑排序判断是否有环。
如何判断“0环是否存在于一条可能在范围内的路径上”呢?这个比较好想,比如当前点是x,
而我们已经判断出了x存在于某个0环内,
那么只要判断1~x+x~n的两段最短路径之和是否>=1~n最短路径+K就好了。。(好想吧?)
因此,我们还需要正着跑一遍dij,倒着跑一遍dij,预处理出两个dis数组,一切都好啦。。
~所以0环的判断就ok了。接下来就是如何计算数目。
我相信这个dp是不难想的,因为考场上我记得我看完题10s内就手抖着写出了方程……= =
但是就是dp如何运行的问题了。
比方说,我们状态这样设计:f[u][k]表示1~u点,比最短路多了k的方案数。
那么很简单,
当(dis[u]+value(u~v))-dis[v]+k<=K(题目给出的K)
那么dp的运行顺序是什么呢?这是当时真正卡住我的点。1~n?不行。n~1?也显然不行。
我们对点的遍历存在某一种顺序,但是这种顺序一开始看起来并不清晰。
考场时,我的第一想法是遍历所有边——因为边可以更新dp值。
但是这是一种刷表的方法,我当时便写成了bfs的形式,后面检查的时候发现这个是只有70的。
因此,我尝试着改进,但是没有治到根本。
顺序怎么定?拓扑排序!
首先,我们只需要规定一个顺序,用拓扑排序即可;但是原图中是有环的,点是加不进所有的,
因此,我们人为规定就好了。
由于这种情况存在解,就是说要么没有0环,要么0环不在最短路中,
前者是不是很好的情况呢?但是为了应付后者,我们应当想到,保留最短路就行了!
没错,把最短路上的边全部都留下来,然后再拓扑排序——存在的环我们之后再更新,
在这之前,只需要找出最短路的所有路径。
然后拓扑排序得到一个顺序。这边是我们dp的顺序。
这是正确的,因为dp的基层是f[u][0],而我们这一步相当于一个基层;
后面以k的逐步增加(f[u][1],f[u][2]……)为顺序,因此dp循环中,k在最外层,
然后按照点的顺序遍历所有的边即可;
即使有环,要求循环更新,我们得出的拓扑序也使得最后的更新偏向n,
每一步更新不必多余走环了。
模拟一下样例的话会很清晰。
考场上就是栽在了调试tarjan和那个bfs上……
其实就算写对了也是70分的。。。
没有想到拓扑还是很讨厌的,主要就是dp顺序没理清。
调试失败的最大问题,就是为了省空间。。。呃
因为有4处使用边表的地方,可以把空间缩到使用2个边表。。然后就多调了30分钟= =
说实话,再做一遍耗了100+分钟……果然退化了。。。
欸。
luogu上70分???最后3个点RE了,我不是很清楚。
但是uojAC了,可能我有点细节出了问题,但是按理来说是没毛病的。
(诶哟卧槽dij都不太会写了)
Day2:
T1:
哈哈哈,这题爆炸实在是骚。
这种并查集不是一眼就看出来了吗???
这种算法不是一大堆吗??
然后嘛……漏了个洞洞穿天破地的情况
就跪了。
no meaning纯属eat shit请无视我。
还是day1T1适合我=_=
T2:
恩很好这题一定要重写。
最遗憾的题!(虽然只差了30分但是。。。)
状压dp太过显然,问题就是我写错了然后最后关头才发现= =嗯。
至于在哪里写错的,我早已经不记得了。所以还是从头到尾重新想一遍。
(其实是不难的)
首先,题目的难点,主要在于价格的计算。
因为目标是构造一棵生成树,所以价格和这棵树的形状是有关系的。。(因此不一定是最小生成树)
假如我们用DP[state]来表示一种状态(state是二进制状态)
那么可以考虑到,在state的基础上,找出k(k不包含在state内),
然后将这个k接到state上即可。
这个需要如何来实现呢?很简单,用DP[state][depth],加上一维来表示深度即可。
注意,在找k的过程中,我们需要枚举k接在哪一个结点上
设这个结点为p,则p一定在state中。
同时需要枚举深度,这样子的话接入后就可以转移了。
其中,dis(p,k)表示p,k间的距离。
当然,p和depth之间需要满足一些条件,因为一个p可能不会满足任意深度,
所以有些深度是不合法的。但是深度又取决于根——
所以我们需要枚举一个根。也就是枚举地面穿洞处。
这样子的话,只要p到根的距离存在此深度(小细节,有时候可以不处理),就可以转移。
值得注意的是,depth代表的是当前点的深度,
如果弄错了,可能出现一个很讨厌的问题:你发现新的一个节点接入时,
找到了一个最优解,深度是3;但实际上接在深度是2的次解上会更优。
所以枚举深度是很必要的!
分析一下复杂度吧。DP数组可以暴力三维,也可以直接滚动了2维就够了的。
所以空间O(n*2^n)
时间上,枚举根,O(n);枚举状态O(2^n),枚举状态中的点O(n)枚举状态外的点O(n)
因此时间复杂度O(n^3*2^n),n=12,完全可以接受。。
至于实现形式,由于其实有很多状态实际上是不会接触到的
(因为状态中必须包含根,这样子就一下子省略了2^(n-1))
所以完全可以用BFS的形式去扩展状态(我认为这样更好理解,和DFS一样是比较本质的)。
另外,输入数据中,很明显点只有12个,
但是边可以多达1000条。
显然是有重边的,因此边权需要取最小值。当然这个属于明显的细节。。。
代码还在调试中!(考试时也是这样……
即使思想清晰明显,代码实力还是摆在那里)
所以把。。。开学后马上来把坑填了!
(代码还不对需要调。。挂着开学后更QAQ……但是基本是对的了(去年考试后同学间早有过确认))
T3:
好家伙……考场上都没有细想过。。。
都是栽在T2上。。。
挂着!想好思路后验证,代码应该要开学后更新了。。