首先发现无论如何操作,只有关于对角线对称的两个位置(ai,j,aj,i)会被交换,而且只有k=i和k=j的操作会影响这两个位置的交换。
我们用si来表示操作k=i是否被执行,如果ai,j<aj,i,我们不希望他们被交换所以我们希望si⊕sj=0,反之如果ai,j>aj,i,我们希望他们交换所以我们希望si⊕sj=1。(⊕代表按位异或)
对于字典序的问题,往往采用从前往后贪心的策略。对于当前的i,j我们要看之前的限制是否允许我们希望的si⊕sj值。那么如何判断呢?我们用一种让并查集的边带权的技巧,即边(u,v)的权值代表su⊕sv,那么一个分量中任意两点的异或值即为路径上边的异或值。为了方便实现我们用节点u代表u到其父节点的边。在find
和join
时要更新边权(见代码)。
条件si⊕sj=x就相当于给i和j中间连一条权值为x的边,对应并查集的join
操作,如果加边之前两个点不在同一个连通分量,那么条件一定是可以满足的。如果在同一个分量中,那么说明si⊕sj已经是确定的,如果si⊕sj=x说明当前条件不能满足,我们就跳过它。
其他实现细节见代码: