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≤108,Q≤105,1≤X≤108N,G,L≤10^8,Q≤10^5,1≤X≤10^8N,G,L≤108,Q≤105,1≤X≤108
solution
先不考虑强制选xxx的情况
先把n/G,L/Gn/G,L/Gn/G,L/G,转化为求gcd=1,lcm=L/Ggcd=1,lcm=L/Ggcd=1,lcm=L/G的方案数
因为L≤1e8L\le 1e8L≤1e8,分解质因子即不超过888个
这么小考虑状压
如何保证gcd=1gcd=1gcd=1
证明某个被选择的皮肤有一个LLL的质因子对应的指数为000,即不含该质因子
如何保证lcm=Llcm=Llcm=L
证明某个被选择的皮肤有一个LLL的质因子对应的指数与LLL相同
LLL的每一个质因子都有某个皮肤指数与之分解后相同
且所有的都不能超过
那么状压分为两类
前一半表示质因子的指数是否为000,后一半表示质因子的指数是否达到LLL上界
显然有些皮肤分解后对应的状态是一样的,统计在一起即可
状态数似乎650650650内
现在加上xxx强制入选的要求,就单列出来
设fff表示前缀积,ggg表示后缀积
ans[i]=val[i→S′]∗∑Sf[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,gf,gf,g用FWTorFWT_{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;
}