[SNOI2017]遗失的答案 (FWT)

description

小皮球在计算出答案之后,买了一堆皮肤,他心里很开心,但是一不小心,就忘记自己买了哪些皮肤了。= =|||
万幸的是,他还记得他把所有皮肤按照 1∼N 来编号,他买来的那些皮肤的编号(他至少买了一款皮肤),最大公约数是 G,最小公倍数是 L。
现在,有 Q 组询问,每组询问输入一个数字 X,请你告诉小皮球,有多少种合法的购买方案中,购买了皮肤 X?
因为答案太大了,所以你只需要输出答案 mod1000000007 即可。

输入格式
第一行,三个数字 N,G,L,如题意所示。
第二行,一个数字 Q,表示询问个数。
第三行,Q 个数字,表示每个询问所问的 X

输出格式
对于每一组询问,在一行中单独输出一个整数,表示这个询问的答案。

样例
Input
5 1 30
5
1 2 3 4 5
Output
1
2
2
0
2

数据范围与提示
30%的数据:N≤20
50% 的数据:N≤1000
70% 的数据:N≤100000
100% 的数据: N , G , L ≤ 1 0 8 , Q ≤ 1 0 5 , 1 ≤ X ≤ 1 0 8 N,G,L≤10^8,Q≤10^5,1≤X≤10^8 N,G,L108,Q105,1X108

solution

先不考虑强制选 x x x的情况

先把 n / G , L / G n/G,L/G n/G,L/G,转化为求 g c d = 1 , l c m = L / G gcd=1,lcm=L/G gcd=1,lcm=L/G的方案数
因为 L ≤ 1 e 8 L\le 1e8 L1e8,分解质因子即不超过 8 8 8
这么小考虑状压

如何保证 g c d = 1 gcd=1 gcd=1
证明某个被选择的皮肤有一个 L L L的质因子对应的指数为 0 0 0,即不含该质因子
如何保证 l c m = L lcm=L lcm=L
证明某个被选择的皮肤有一个 L L L的质因子对应的指数与 L L L相同
L L L的每一个质因子都有某个皮肤指数与之分解后相同
且所有的都不能超过

那么状压分为两类
前一半表示质因子的指数是否为 0 0 0,后一半表示质因子的指数是否达到 L L L上界

显然有些皮肤分解后对应的状态是一样的,统计在一起即可
状态数似乎 650 650 650

现在加上 x x x强制入选的要求,就单列出来
f f f表示前缀积, g g g表示后缀积
a n s [ i ] = v a l [ i → S ′ ] ∗ ∑ S f [ i − 1 ] [ S ] × g [ i + 1 ] [ S ] ans[i]=val[i\rightarrow S']*\sum_Sf[i-1][S]\times g[i+1][S] ans[i]=val[iS]Sf[i1][S]×g[i+1][S]
发现可以将 f , g f,g f,g F W T o r FWT_{or} FWTor卷起来
在这里插入图片描述

具体可看代码

code

代码经过各种取模优化,快读优化,吸氧才堪堪跑过

#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
#define mod 1000000007
vector < int > num;
int n, G, L, Q, N, cnt, tot;
int p[10], e[10];
int id[650], s[1 << 16], sum[650], ans[650];
int h[650][1 << 16], f[650][1 << 16], g[650][1 << 16];

void read( int &x ) {
    
    
	x = 0; int f = 1; char s = getchar();
	while( s < '0' || s > '9' ) {
    
    
		if( s  == '-' ) f = -1;
		s = getchar();
	}
	while( '0' <= s && s <= '9' ) {
    
    
		x = ( x << 1 ) + ( x << 3 ) + ( s - '0' );
		s = getchar();
	}
	x *= f;
}

int qkpow( int x, int y ) {
    
    
	int ans = 1;
	while( y ) {
    
    
		if( y & 1 ) ans = 1ll * ans * x % mod;
		x = 1ll * x * x % mod;
		y >>= 1;
	}
	return ans;
}

void init( int x ) {
    
    
	for( int i = 2;i * i <= x;i ++ ) {
    
    
		if( x % i == 0 ) {
    
    
			p[++ tot] = i;
			while( x % i == 0 ) x /= i, e[tot] ++;
		}
	}
	if( x > 1 ) p[++ tot] = x, e[tot] = 1;
}

int calc( int x ) {
    
    
	int S = 0;
	for( int i = 1;i <= tot;i ++ ) {
    
    
		int t = 0;
		while( x % p[i] == 0 ) x /= p[i], t ++;
		if( t == 0 ) S |= ( 1 << ( i - 1 ) );
		if( t == e[i] ) S |= ( 1 << ( i - 1 + tot ) );
	}
	return S;
}

int add( int x, int y ) {
    
    
	x += y;
	if( x > mod ) return x - mod;
	return x;
}

int sub( int x, int y ) {
    
    
	x -= y;
	if( x < 0 ) return x + mod;
	return x;
}

void FWT_or( int *v, int f ) {
    
    
	for( int i = 1;i < N;i <<= 1 )
		for( int j = 0;j < N;j += ( i << 1 ) )
			for( int k = 0;k < i;k ++ )
				if( f == 1 ) v[j + k + i] = add( v[j + k + i], v[j + k] );
				else v[j + k + i] = sub( v[j + k + i], v[j + k] );
}

int main() {
    
    
	read( n ), read( G ), read( L ), read( Q );
	if( L % G ) {
    
    
		while( Q -- ) {
    
    
			int x;
			read( x );
			printf( "0\n" );
		}
		return 0;
	}
	L /= G, n /= G, init( L );
	for( int i = 1;i <= n && i * i <= L;i ++ ) {
    
    
		if( L % i ) continue;
		num.push_back( i );
		if( L / i <= n && i * i != L )
			num.push_back( L / i );
	}
	for( int i = 0;i < num.size();i ++ )
		s[calc( num[i] )] ++;
	N = 1 << ( tot << 1 ); 
	for( int i = 0;i < N;i ++ )
		if( s[i] ) id[++ cnt] = i, sum[cnt] = qkpow( 2, s[i] ) - 1;
	f[0][0] = g[cnt + 1][0] = 1;
	for( int i = 1;i <= cnt;i ++ )
		for( int S = 0;S < N;S ++ ) {
    
    
			f[i][S] = add( f[i][S], f[i - 1][S] );
			f[i][S | id[i]] = add( f[i][S | id[i]], 1ll * f[i - 1][S] * sum[i] % mod );
		}
	for( int i = cnt;i;i -- )
		for( int S = 0;S < N;S ++ ) {
    
    
			g[i][S] = add( g[i][S], g[i + 1][S] );
			g[i][S | id[i]] = add( g[i][S | id[i]], 1ll * g[i + 1][S] * sum[i] % mod );
		}
	for( int i = 0;i <= cnt;i ++ ) FWT_or( f[i], 1 );
	for( int i = 1;i <= cnt + 1;i ++ ) FWT_or( g[i], 1 );
	for( int i = 1;i <= cnt;i ++ )	
		for( int S = 0;S < N;S ++ )
			h[i][S] = 1ll * f[i - 1][S] * g[i + 1][S] % mod;
	for( int i = 1;i <= cnt;i ++ ) FWT_or( h[i], -1 );
	for( int i = 1;i <= cnt;i ++ ) {
    
    
		for( int S = 0;S < N;S ++ )
			if( ( S | id[i] ) == N - 1 )
				ans[i] = add( ans[i], h[i][S] );
		ans[i] = 1ll * ans[i] * qkpow( 2, s[id[i]] - 1 ) % mod;
	}
	while( Q -- ) {
    
    
		int x;
		read( x );
		if( x % G ) {
    
    
			printf( "0\n" );
			continue;
		}
		x /= G;
		if( L % x || x > n ) {
    
    
			printf( "0\n" );
			continue;
		}
		int p = lower_bound( id + 1, id + cnt + 1, calc( x ) ) - id;
		printf( "%d\n", ans[p] );
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Emm_Titan/article/details/113686198
FWT
今日推荐