Topcoder 2016 TCO Algorithm Algo Semifinal 2 Hard

链接:https://community.topcoder.com/stat?c=problem_statement&pm=14436&rd=16841

题意:对于一个大小为k的线性基,可以构造出2 ^ k种不同的异或,其中第rank[i]小的为value[i],求有多少个线性基满足要求。

题解:

注意分析线性基的性质

1、显然rank和value是可以直接消的,先把rank消成主对角线和一些自由元;

2、rank[i]的最高位对应的基(的位置)一定是value[i]的最高位,记为must

3、f(i, j)表示当前考虑第i位(前i - 1位已经考虑完了),考虑第j个基(前j - 1个基已经考虑完了)的方案数

4、考虑从f(i, j)转移到f(i + 1, j / j + 1)

显然是f(i + 1, j) += f(i + 1, j + 1),我们只考虑f(i, j)到f(i + 1, j + 1)

先判是否i作为第j + 1个基是合法的,如果合法如果must[j] == i,直接转移

如果must[j]没有限制,那么这个基里有2 ^ (i - j)种放法(共有2 ^ i种,j个基必须按照要求放)

最好记一个g(i, j)表示f(i, j)是否合法,这样不会出现f(i, j) % mod = 0而f(i, j) != 0的情况,否则判inf可能会挂

#include <bits/stdc++.h>
#define xx first
#define yy second
#define mp make_pair
#define pb push_back
#define fill( x, y ) memset( x, y, sizeof x )
#define copy( x, y ) memcpy( x, y, sizeof x )
using namespace std;

typedef long long LL;
typedef pair < int, int > pa;

const int mod = 1e9 + 7;

int f[35][35], must[35];
bool g[35][35], lb[35];

inline void inc(int &x, int y) { x += y; if( x >= mod ) x -= mod; }

class XorRank
{
	public:
		int count(int m, vector < int > rk, vector < int > val)
		{
			for( int i = 0 ; i < m ; i++ ) must[ i ] = -1;
			int n = rk.size(), rk_max = 0, inf = 0;
			for( int i = 0 ; i < n ; i++ ) rk_max = max( rk_max, rk[ i ] );
			inf = rk_max < ( 1 << m - 1 );
			for( int i = m - 1 ; ~i ; i-- )
				for( int j = 0 ; j < n ; j++ ) if( !lb[ j ] && rk[ j ] >> i & 1 )
				{
					lb[ j ] = 1;
					for( int k = 0 ; k < n ; k++ ) if( j ^ k ) if( rk[ k ] >> i & 1 ) rk[ k ] ^= rk[ j ], val[ k ] ^= val[ j ]; 
					break;
				}
			for( int i = 0 ; i < n ; i++ ) if( ( !rk[ i ] && val[ i ] ) || ( rk[ i ] && !val[ i ] ) ) return 0;
			for( int i = 0 ; i < n ; i++ ) if( rk[ i ] )
			{
				int high_bit = -1, high_val = -1;
				for( int j = m - 1 ; ~j ; j-- ) if( rk[ i ] >> j & 1 ) { high_bit = j; break; }
				for( int j = 29 ; ~j ; j-- ) if( val[ i ] >> j & 1 ) { high_val = j; break; }
				must[ high_bit ] = high_val;
			}
			f[ 0 ][ 0 ] = g[ 0 ][ 0 ] = 1;
			for( int i = 0 ; i < 30 ; i++ )
				for( int j = 0 ; j <= m ; j++ ) if( g[ i ][ j ] )
				{
					g[ i + 1 ][ j ] = 1;
					inc( f[ i + 1 ][ j ], f[ i ][ j ] );
					bool flag = true;
					for( int k = 0 ; k < n ; k++ )
						if( ( rk[ k ] >> j & 1 ) ^ ( val[ k ] >> i & 1 ) ) { flag = false; break; }
					if( flag )
					{
						if( must[ j ] == i ) inc( f[ i + 1 ][ j + 1 ], f[ i ][ j ] ), g[ i + 1 ][ j + 1 ] = 1;
						if( !~must[ j ] ) inc( f[ i + 1 ][ j + 1 ], ( 1LL << i - j ) * f[ i ][ j ] % mod ), g[ i + 1 ][ j + 1 ] = 1;
					}
				}
			if( !g[ 30 ][ m ] ) return 0;
			if( inf ) return -1;
			return f[ 30 ][ m ];
		}
};


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值