第三次体会到考试出超纲题的绝望……
不要问我前两次在什么时候
\(\mathrm{T1}\)
\(\color{red}{Totally\ Brute\ Force!}\)
就是个暴力……
我们一开始假设整个数组是一个区间\([1,n]\)的区间,然后每次操作都会把区间从中间断开。(可以理解成:把中间的数删去后,左右两侧的数会分成两个区间)
这里要说一下这些区间的定义:
struct Segment{int st,len;};
\(st\)表示这个区间中最小的数;\(len\)表示这个区间的长度。
比如说,原题的样例\(1\),进行了第一次删除操作后,整个数组就被分成了两个区间:分别是\(1\sim2,5\sim8\)。用结构体表示就是
(Segment){1,2};
(Segment){5,4};
然后,假如我们要处理一个删除操作,这个删除操作会影响到连续的若干个区间的话,有两个操作是所有情况通用的:
- 修改最左区间的\(len\)
- 新建一个区间。
这个区间的\(st\)是数组中的第\(ri\)个数(\(ri\)就是删除操作的\(ri\)),但这个第\(ri\)个数的准确数值要从
Segment
那个结构体中得到(因为\(st\)存的是准确数值)。这个区间的\(len\)就是受到影响的最右边的区间的\(len-\text{最右边的区间被删去部分的长度}\)
也许你会问:为什么不直接修改最右边的区间呢?
假如删除操作只影响了一个区间,那么这个区间就会分裂成两个区间,所以还是要新建一个区间的。
做完上述两个操作之后,就把受到影响的,且既不是最左也不是最右的区间删掉。
删完之后,再看一下:如果最左区间或者最右区间的\(len=0\),就把这个区间也删掉。
插入删除操作就跟在顺序线性表中插入元素一样,时间复杂度是\(\mathrm{O}(n)\)的。
重点其实应该是修改最左区间和插入最右区间这两个操作。
看代码吧:\(\mathrm{Code}\)
\(\mathrm{T2}\)
这就是超纲题了。
建议大家先看看这一题和这一题并尝试将代码写出来再继续往下看。
回到这一题。
很明显,\(\mathrm{T}\)很大,而每条边的权值又很小,这时,同余系下最短路就能发挥用场了。
在跳楼机那一题中,我们用\(dis[i]\)表示路径长度在模一个指定边权之后的数值为\(i\)的最小值。(其实和分层图有点像)
在这里,我们只需要把\(dis[i]\)扩多一维变成\(dis[i][j]\),\(i\)表示在第\(i\)个节点,\(j\)的意义和上面的\(i\)一样。
然后我们就可以像跳楼机一样猥琐操作了:
- 首先,找到 与起点或终点直接相连的边的最小边权\(len\) 作为模数(这样做是为了加快后面的\(\mathrm{SPFA}\))。
- 然后就像分层图上最短路一样直接跑\(\mathrm{SPFA}\)。
具体来讲就是
if( dis[h.pos][h.mval]+e.len<dis[e.to][(h.mval+e.len)%mod] ) dis[e.to][(h.mval+e.len)%mod]=dis[h.pos][h.mval]+e.len; //从代码里抄的
- 跑完\(\mathrm{SPFA}\)后,我们要取的是\(dis[n-1][\mathrm{T}\%(len \times 2)]\)的值,这样的话,\(dis\)中最短路的长度加上若干个\(len\)之后就会等于\(\mathrm{T}\)了。
问题来了:为什么是\(\%(len \times 2)\)而不是\(\%len\)呢?
- 因为我们到了终点或者还停留在起点时,只有在同一条边上走偶数次才能回到原处。
接着,我们可以把\(\mathrm{T}\)变形成一次函数的形式(就是\(ax+b\ (a=e.len,b<a)\))。
我们在同一条边上走偶数次,就等同于将\(\mathrm{T}=ax+b\)这个式子的\(a\)减去了一个偶数。
设被减去后的\(\mathrm{T}=a'x+b\),则很明显:
\(a' \equiv a \pmod 2\)
(就是说\(a'\)和\(a\)奇偶性相同)换言之,我们要使\(dis\)数组中的最短路长度的\(a\)和\(\mathrm{T}\)的\(a\)同奇同偶。
然后,下面的结论就
很容易证明了:设\(\mathrm{T}=a_1x+b,\mathrm{T}\%(len\times 2)=a_2x+b\),则\(a_1 \equiv a_2 \pmod{2}\)。
所以我们要做的就是:
- 找到与起点或终点直接相连的边的最小边权\(len\);
- 以\(len\)作为模数跑剩余系最短路
- 判断\(dis[n-1][\mathrm{T}\%(len \times 2)]\)是否\(\leqslant \mathrm{T}\)。
大概就是这样了,剩下的细节就看看代码吧
(对自己代码的思维清晰程度总是有着谜一般的信心)
\(\mathrm{T3}\)
其实这一题是码量最短的。
一眼看过去以为是博弈论,但随后发现其实就是一个贪心:
以\(\mathrm{T}\)节点为根,我们就可以一层一层地推(假设\(\mathrm{T}\)节点的深度为\(0\))
- 第\(1\)层最多只能放\(1\)个棋子;
- 第\(2\)层最多只能放\(3\)个棋子;
- 第\(3\)层最多只能放\(7\)个棋子;
- 。。。
- 第\(n\)层就最多能放\(2n-1\)个棋子。
但是:如果一个节点有多于一个儿子的话,那我们就不能每个节点都来一个\(2n+1\)了。
正确的转移方法是:找到所有儿子所在的子树中,深度最深的子树,把\(2n+1\)个棋子转移到这个最深的儿子。其他的儿子就直接只给\(1\)个棋子,然后再重新转移。
正确性容易证明。
原文地址:https://www.cnblogs.com/info---tion/p/11281428.html