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,L≤108,Q≤105,1≤X≤108
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 L≤1e8,分解质因子即不超过 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[i→S′]∗S∑f[i−1][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;
}