「Gym103069C」Random Shuffle
作者:互联网
题目
点这里看题目。
分析
关键观察在于,这道题的 \(n\) 居然有一个较大的下界!!!正常题目 \(n\) 的最小值一般都是个位数,这道题样例中 \(n=50\)??说明这道题思路必然是通过 \(a\) 得到关于 \(x\) 的若干位的限制,然后暴力枚举检验。这样才能解释 \(n\) 为什么无法取到较小的值。
接着可以手玩发现,我们实际上可以反推出一次 rand
之前的 \(x\)。进一步地,我们考察若干次 rand
后的 \(x\) 和初始 \(x\) 的关系——其实就是一个线性变换的关系,并且这个变换必然是可逆的。所以,只要我们能够精准地给出某次 rand
后 \(x\) 的若干位,我们就能得到相应的方程。
如何精确地给出 \(x\) 的一位?我们可以通过 \(a\) 得到第 \(k\) 次 rand
过后 \(x\bmod k\) 的结果。注意到如果 \(k=2^cr,2\nmid r\),则我们也可以得到 \(x\bmod 2^c\) 的结果,进而得到 \(x\) 的低 \(c\) 位。当 \(n=50\) 的时候,我们至多可以得到 \(\sum_{k\ge 1}\lfloor\frac{n}{2^k}\rfloor=47\) 位,这样只需要枚举 \(17\) 位。
如何证明这 \(47\) 条方程线性无关?把前 \(50\) 个矩阵拉出来验证一下就好了。另外,这个矩阵实际上一定会出现幂次循环。不过当它会出现循环的时候,可以想象指数已经相当大了,我们已经得到了足够的信息来准确地给出 \(x\)。
这里动态地加入方程,其实不需要最后一块解,可以动态维护消元之后的结果,再使用 unsigned long long
优化一下即可。
小结:
-
对于数据范围的观察,很多时候都能得到意料之外的启发!
-
动态维护方程组的小技巧,记录一下;动态维护可以避免之后一块算。
不演了,这就是消元法,只不过变量少从而限制了线性无关的方程少而已。
代码
#include <cstdio>
#include <cassert>
#include <iostream>
#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )
typedef unsigned long long ull;
const int MAXN = 1e5 + 5, BIT = 64;
template<typename _T>
void read( _T &x ) {
x = 0; char s = getchar(); int f = 1;
while( ! ( '0' <= s && s <= '9' ) ) { f = 1; if( s == '-' ) f = -1; s = getchar(); }
while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
x *= f;
}
template<typename _T>
void write( _T x ) {
if( x < 0 ) putchar( '-' ), x = -x;
if( 9 < x ) write( x / 10 );
putchar( x % 10 + '0' );
}
struct Equation {
ull vec; bool res;
Equation(): vec( 0 ), res( 0 ) {}
Equation( ull V, bool R ): vec( V ), res( R ) {}
};
ull fre;
Equation equ[64];
ull mat[MAXN][64];
int perm[MAXN], pos[MAXN];;
int A[MAXN];
int N;
namespace Validator {
ull seed;
int tmp[MAXN];
inline ull Rand() {
seed ^= seed << 13;
seed ^= seed >> 7;
seed ^= seed << 17;
return seed;
}
inline bool Validate( const ull &X ) {
seed = X;
rep( i, 1, N ) {
tmp[i] = i;
std :: swap( tmp[i], tmp[Rand() % i + 1] );
}
rep( i, 1, N )
if( tmp[i] != A[i] )
return false;
return true;
}
}
inline Equation operator ^ ( const Equation &a, const Equation &b ) {
return Equation( a.vec ^ b.vec, a.res ^ b.res );
}
inline Equation& operator ^= ( Equation &a, const Equation &b ) {
return a = a ^ b;
}
inline void AddEquation( Equation nw ) {
rep( i, 0, BIT - 1 )
if( nw.vec >> i & 1 ) {
if( ! equ[i].vec ) {
equ[i] = nw;
break;
}
nw ^= equ[i];
}
}
inline ull Generate( const ull &freState ) {
ull ret = freState;
rep( k, 0, BIT - 1 ) if( equ[k].vec ) {
int idx = __builtin_ctzll( equ[k].vec );
ret ^= ( 1llu * ( __builtin_parityll( equ[k].vec & freState ) ^ equ[k].res ) ) << idx;
}
return ret;
}
int main() {
read( N );
rep( i, 1, N ) {
read( A[i] );
perm[i] = A[i], pos[perm[i]] = i;
}
rep( i, 0, BIT - 1 ) mat[0][i] = 1llu << i;
rep( i, 1, N )
rep( j, 0, BIT - 1 ) {
mat[i][j] = mat[i - 1][j];
mat[i][j] ^= mat[i][j] << 13;
mat[i][j] ^= mat[i][j] >> 7;
mat[i][j] ^= mat[i][j] << 17;
}
per( i, N, 1 ) {
int r = pos[i] - 1;
std :: swap( pos[i], pos[perm[i]] );
std :: swap( perm[pos[perm[i]]], perm[i] );
int t = __builtin_ctz( i );
rep( j, 0, t - 1 ) {
Equation tmp;
tmp.res = r >> j & 1;
rep( k, 0, BIT - 1 )
tmp.vec |= ( mat[i][k] >> j & 1 ) << k;
AddEquation( tmp );
}
}
fre = BIT == 64 ? -1 : ( 1llu << BIT ) - 1;
rep( k, 0, BIT - 1 ) if( equ[k].vec ) {
int idx = __builtin_ctzll( equ[k].vec );
fre ^= 1llu << idx;
rep( j, 0, BIT - 1 )
if( j ^ k && equ[j].vec >> idx & 1 )
equ[j] ^= equ[k];
}
bool ever = false; ull ans;
for( ull s = fre ; s ; s = ( s - 1 ) & fre ) {
ans = Generate( s );
if( Validator :: Validate( ans ) ) {
ever = true; break;
}
}
if( ! ever ) {
ans = Generate( 0 );
if( Validator :: Validate( ans ) )
ever = true;
}
assert( ever );
write( ans ), putchar( '\n' );
return 0;
}
标签:Shuffle,int,Random,equ,Gym103069C,MAXN,vec,ull,ans 来源: https://www.cnblogs.com/crashed/p/16403572.html