• 「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 优化一下即可。

    小结:

    1. 对于数据范围的观察,很多时候都能得到意料之外的启发!

    2. 动态维护方程组的小技巧,记录一下;动态维护可以避免之后一块算。

      不演了,这就是消元法,只不过变量少从而限制了线性无关的方程少而已。

    代码

    #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;
    }
    
  • 相关阅读:
    对Item中定时器的理解
    ClassLoader类加载机制&&JVM内存管理
    基于Quartz实现简单的定时发送邮件
    基于NIO的Socket通信
    1、svn架设、基本命令
    sysbench基准测试(2)——oltp.lua测试
    sysbench基准测试工具使用
    1、linux软件包管理
    7、数据结构五:sorted sets
    6、数据类型四:sets
  • 原文地址:https://www.cnblogs.com/crashed/p/16403572.html
Copyright © 2020-2023  润新知