随机打乱
概述
-
某些题目的数据,如果顺序随机,将会有非常美妙的结论。
-
但显然,除非写了“保证数据随机”(事实上,没给 generator 的随机都可以认为是构造...),否则出题人不会这样给数据。
-
此时就要考虑将数据重排,以获得较优的效果。
如何随机打乱?
- 给出一种均匀随机的打乱算法及证明:
template <typename T>
il void ran(T a[],int n){
foR(i,n,2)
swap(a[i],a[_rand()%i+1]);
return;
}
-
显然,当 \(n=1\),该算法均匀随机。接下来我们考虑归纳证明。
-
首先,我们可以把上面那个算法改写成递归形式:
template <typename T>
il void ran(T a[],int n){
if(n==1) return;
swap(a[n],a[_rand()%n+1]);
ran(a,n-1);
return;
}
-
显然,“均匀随机”等价于对于所有的 \(n\) 的排列 \(\pi,P(a_{1\sim n}=\pi)=\dfrac{1}{n!}\)。
-
尝试归纳证明。
-
考虑把 \(n+1\) 的排列中的 \(n+1\) 删去,发现恰有 \(n+1\) 种 \(\pi'\)(分别对应 \(n+1\) 在 \(1,2,\dots,n+1\))在删除后得到的 \(\pi\) 相同,且这些 \(\pi'\) 不同。
-
从而得证,对于所有的 \(n+1\) 的排列 \(\pi',P(a_{1\sim n+1}=\pi')=\dfrac{1}{(n+1)!}\)。故该算法均匀随机。
-
当然,也可以用
random_shuffle
,该函数内部实现其实就是上面的算法。可以在 笔记-语法 里找到。
例题
P7606 混乱邪恶
-
题意:
-
给定一个六边形网格图。可以认为初始时在 \((0,0)\)。你有两种属性 \((L,G)\),初始时属性为 \((0,0)\)。
-
可以移动 \(n\) 次,每次移动有六种决策,第 \(i\) 种会使属性改变 \((\Delta_{i,1},\Delta_{i,2})\)(直接相加)。这里的加法是在 \(\bmod\ p\) 意义下进行的。
-
问是否存在一种方案,使得在 \(n\) 步后回到 \((0,0)\),且属性为给出的 \((L^* ,G^* )\)。
-
-
数据范围:
-
\(1\leqslant n,p\leqslant 100\)。
-
其他数据 \(<p\)。
-
\(700ms\) 时限。
-
-
首先我们需要对六边形网格图进行坐标规定,否则没法做。一个常见的编码方法是:
-
右上,\((+1,+1)\)。
-
右,\((+1,0)\)。
-
右下,\((0,-1)\)。
-
左下,\((-1,-1)\)。
-
左,\((-1,0)\)。
-
左上,\((0,+1)\)。
-
-
其实就是认为右是 \(x\) 轴正方向,左上是 \(y\) 轴正方向,右上是 \(l:y=x\)。当然编码方式很多的,随便用一个就好啦。
-
然后...然后这个东西怎么看都很 dp 吧?
-
状态设计:\(dp_{i,x,y,L,G}\) 表示走了 \(i\) 步,当前在 \((x,y)\),当前的属性为 \((L,G)\),可不可行。
-
初始化:\(dp_{0,0,0,0,0}=1\)。
-
递推转移:\(dp_{i,x,y,L,G}\to dp_{i+1,x+dx,y+dy,L+dl,G+dg}\)。
-
-
状态数 \(n^5\),转移 \(O(6)\) 不过可能跑不满。但这显然无法接受...考虑用
bitset
压一下,于是大约 \(10^9\) 的复杂度,还是无法接受啊? -
注意到一个显然的事情,如果 \((x,y)\) 中某一维的绝对值大于还能走的步数,该状态无意义。毕竟走不回来。虽然如此,状态还是挺密集的,分层图 bfs 大概是负优化。
-
铛铛!有一个重要结论:随机游走 \(n\) 步恰回到起点的坐标绝对值期望为 \(\sqrt{n}\)。可以证但我不会,原命题是一维数轴或二维平面情况,但显然推广到这里也是对的。
-
考虑随机重排所有决策,显然,决策的顺序对结果没有影响。从而接下来就是一个接近随机游走的情况啦!
-
可以把 \(dp\) 的第二,三维都开到 \(2\sqrt{n}\) 多一点就好。于是复杂度肯定是 \(O(\text{能过})\),额...大概 \(4\times10^7\)?
平面最近点对(系列)
- 见随机排序。