「SNOI2017」遗失的答案

「SNOI2017」遗失的答案

传送门

Loj

题解

首先可以考虑把\(n \rightarrow n/G,L \rightarrow L/G\),这是很显然的.

然后考虑所有可能出现的质因数就是\(L\)的质因数,所以可以对\(L\)进行质因数分解.

接着考虑设\(f_{S1,S2}\)表示最小集为\(S1\),最大集为\(S2\)的状态的方案数,这个很显然可以\(dp\).

这个时候我们需要强制一个数字在里面,相当于是强制他不在里面然后查他的超集的方案数.

所以可以前后缀\(dp\)然后做一个\(FMT\),求出来每一个不在里面的方案数,最后做一个超集和就可以了.

代码

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<iostream>
using namespace std;
#define ll long long
#define REP(a,b,c) for(int a=b;a<=c;a++)
#define re register
#define file(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout)
typedef pair<int,int> pii;
#define mp make_pair
inline int gi()
{
	int f=1,sum=0;char ch=getchar();
	while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
	return f*sum;
}
const int N=10010,LIM=1<<16,MX=18,Mod=1e9+7,INV=500000004;
int n,G,L,Q,prime[N],s,p[N],mx[N],tot,cnt[LIM];
bool is_prime[N];
void init()
{
	is_prime[1]=1;
	for(int i=2;i<=10000;i++)
	{
		if(!is_prime[i])prime[++s]=i;
		for(int j=1;j<=s&&prime[j]*i<=10000;j++)
		{
			is_prime[prime[j]*i]=1;
			if(!(i%prime[j]))break;
		}
	}
}
void calc(int x)
{
	for(int i=1;i<=s;i++)
		if(x%prime[i]==0)
		{
			p[++tot]=prime[i];
			while(x%prime[i]==0)x/=prime[i],mx[tot]++;
		}
	if(x>1)p[++tot]=x,mx[tot]=1;
}
void dfs(int d,int s,int S1,int S2)
{
	if(d>tot){cnt[S1|(S2<<tot)]++;return;}
	for(int i=0;i<=mx[d];i++)
	{
		dfs(d+1,s,S1|(i==0?1<<(d-1):0),S2|(i==mx[d]?1<<(d-1):0));
		if(s*p[d]>n)break;
		s*=p[d];
	}
}
int o[LIM],S[LIM],suf[810][LIM],pre[810][LIM],f[LIM],tmp[LIM];
int qpow(int a,int b){int ret=1;while(b){if(b&1)ret=1ll*ret*a%Mod;b>>=1;a=1ll*a*a%Mod;}return ret;}
void FMT(int *a,int limit,int opt)
{
	for(int i=1;i<limit;i<<=1)
		for(int R=i<<1,j=0;j<limit;j+=R)
			for(int k=0;k<i;k++)
				if(opt==1)a[i+j+k]=(a[i+j+k]+a[j+k])%Mod;
				else a[i+j+k]=(a[i+j+k]-a[j+k]+Mod)%Mod;
}
void FAT(int *a,int limit,int opt)
{
	for(int i=1;i<limit;i<<=1)
		for(int R=i<<1,j=0;j<limit;j+=R)
			for(int k=0;k<i;k++)
				if(opt==1)a[j+k]=(a[j+k]+a[i+j+k])%Mod;
				else a[j+k]=(a[j+k]-a[i+j+k]+Mod)%Mod;
}
int get(int x)
{
	int S1=0,S2=0;
	for(int i=1;i<=tot;i++)
	{
		int cnt=0;
		while(x%p[i]==0)x/=p[i],cnt++;
		if(cnt==0)S1|=1<<(i-1);
		if(cnt==mx[i])S2|=1<<(i-1);
	}
	return S1|(S2<<tot);
}
int main()
{
	n=gi();G=gi();L=gi();Q=gi();init();
	if(L%G){while(Q--)puts("0");return 0;}
	L/=G;n/=G;calc(L);dfs(1,1,0,0);int lim=1<<(tot<<1);s=0;
	for(int i=0;i<lim;i++)
		if(cnt[i])o[++s]=i,S[s]=qpow(2,cnt[i])-1;
	for(int i=1;i<=s;i++)cnt[i]=S[i];
	f[0]=1;pre[0][0]=1;
	for(int i=1;i<=s;i++)
	{
		int x=o[i];
		for(int j=0;j<lim;j++)
			tmp[j|x]=(tmp[j|x]+1ll*f[j]*cnt[i]%Mod)%Mod;
		for(int j=0;j<lim;j++)f[j]=(f[j]+tmp[j])%Mod,tmp[j]=0;
		for(int j=0;j<lim;j++)pre[i][j]=f[j];
	}
	memset(f,0,sizeof(f));
	f[0]=1;suf[s+1][0]=1;
	for(int i=s;i;i--)
	{
		int x=o[i];
		for(int j=0;j<lim;j++)
			tmp[j|x]=(tmp[j|x]+1ll*f[j]*cnt[i]%Mod)%Mod;
		for(int j=0;j<lim;j++)f[j]=(f[j]+tmp[j])%Mod,tmp[j]=0;
		for(int j=0;j<lim;j++)suf[i][j]=f[j];
	}
	for(int i=0;i<=s;i++)FMT(pre[i],lim,1);
	for(int i=1;i<=s+1;i++)FMT(suf[i],lim,1);
	for(int i=0;i<=s;i++)
		for(int j=0;j<lim;j++)
			pre[i][j]=1ll*pre[i][j]*suf[i+2][j]%Mod;
	for(int i=0;i<=s;i++)FMT(pre[i],lim,-1),FAT(pre[i],lim,1);
	while(Q--)
	{
		int x=gi();
		if(x%G){puts("0");continue;}
		x/=G;
		if(L%x||x>n){puts("0");continue;}
		int ss=get(x),p=lower_bound(o+1,o+s+1,ss)-o-1;
		int v=pre[p][(lim-1)^ss];
		printf("%lld\n",1ll*v*(cnt[p+1]+1)%Mod*INV%Mod);
	}
	return 0;
}
posted @ 2020-05-07 22:08  fexuile  阅读(130)  评论(0编辑  收藏  举报