Luogu P5366 [SNOI2017]遗失的答案

题目
筛出\(L\)的每个质因子,限制条件实际上就是限制了每个质因子的出现次数\([l,r]\),且\(l,r\)都有正好取到的。
显然\(L\)最多有\(8\)个不同的因子,所以对每个数而言,我们对其每个质因子是否取到\(l\)\(r\)状压,这样\([1,n]\)的数就可以表示成一个\(16\)位的二进制数。
那么我们要求的就是在强制选某个二进制数的情况下,选出一些二进制数使得其按位或为全集的方案数。
\(f(S)\)表示选出一些二进制数使得其按位或为\(S\)的方案数。
考虑容斥,设\(g(S)=\sum\limits_{T\subseteq S}f(T)\)
那么显然的,记\(cnt_S\)表示有多少个二进制数是\(S\)的子集,\(g(S)=2^{cnt_S}\)
那么我们容斥即可得到\(f(S)=\sum\limits_{T\subseteq S}(-1)^{|S|-|T|}g(T)\)
然后考虑计算强制选择某个二进制数时的答案。
设这个数为\(T\),其补集为\(S\)
那么我们需要枚举的就是\(S\)的超集(即子集包含\(S\)的集合)。
这个可以转化为枚举\(S\)的子集,然后枚举的超集就是\(T\cup S\)
加上记忆化之后复杂度为\(3^{16}\)

#include<bits/stdc++.h>
#define pi pair<int,int>
#define pb push_back
#define fi first
#define se second
#define count __builtin_popcount
using namespace std;
const int N=100007,P=1000000007;
namespace IO
{
    char ibuf[(1<<21)+1],obuf[(1<<21)+1],st[11],*iS,*iT,*oS=obuf,*oT=obuf+(1<<21);
    char Get(){return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++);}
    void Flush(){fwrite(obuf,1,oS-obuf,stdout),oS=obuf;}
    void Put(char x){*oS++=x;if(oS==oT)Flush();}
    //int read(){int x=0,c=Get();while(!isdigit(c))c=Get();while(isdigit(c))x=x*10+c-48,c=Get();return x;}
    void write(int x){int top=0;if(!x)Put('0');while(x)st[++top]=(x%10)+48,x/=10;while(top)Put(st[top--]);Put('\n');}
}
using namespace IO;
int read(){int x;cin>>x;return x;}
int inc(int a,int b){a+=b;return a>=P? a-P:a;}
int dec(int a,int b){a-=b;return a<0? a+P:a;}
int mul(int a,int b){return 1ll*a*b%P;}
int n,G,L,Q,l[N],r[N],p2[N],sz[N],U,len,ans[1<<17];vector<pi>fac;unordered_map<int,int>mp;
int GetS(int x)
{
    int S=0,i,cnt;
    for(i=0;i<len;++i)
    {
        for(cnt=0;!(x%fac[i].fi);++cnt) x/=fac[i].fi;
	S|=(cnt==l[i])<<i|(cnt==r[i])<<i+len;
    }
    return S;
}
void initfac(int x=L)
{
    for(int i=2,cnt=0;i*i<=x;++i)
	if(!(x%i))
	{
	    for(cnt=0;!(x%i);x/=i,++cnt);
	    fac.pb(pi(i,cnt));
	}
    if(x^1) fac.pb(pi(x,1));
}
void initpow()
{
    p2[0]=1,len=fac.size(),U=(1<<len*2)-1;
    for(int i=1;i<=U;++i) p2[i]=inc(p2[i-1],p2[i-1]);
}
void initlr()
{
    for(int i=0,x;i<len;++i)
    {
	r[i]=fac[i].se;
	for(x=G;!(x%fac[i].fi);++l[i]) x/=fac[i].fi;
    }
}
void initagg()
{
    for(int i=1;i*i<=L;++i)
	if(!(L%i))
	{
	    if(!(i%G)&&i<=n) mp[i]=GetS(i);
	    if(i*i^L&&!(L/i%G)&&L/i<=n) mp[L/i]=GetS(L/i);
	}
    len<<=1;
    for(auto x:mp) ++sz[x.se];
    for(int i=0,S;i<len;++i) for(S=0;S<=U;++S) if(S&1<<i) sz[S]+=sz[S^1<<i];
}
int solve(int x)
{
    int T=mp[x],S=U^T,sum=count(T)&1? (P-p2[sz[T]-1]):(p2[sz[T]-1]);
    if(ans[T]) return ans[T];
    for(int s=S;s;s=s-1&S)
	if(count(s|T)&1) sum=dec(sum,p2[sz[s|T]-1]);
	else sum=inc(sum,p2[sz[s|T]-1]);
    return ans[T]=sum;
}
int main()
{
    n=read(),G=read(),L=read(),Q=read();
    if(L%G) { while(Q--) puts("0"); return 0; }
    initfac(),initpow(),initlr(),initagg();
    for(int x;Q;--Q) x=read(),write(x%G||L%x||x>n? 0:solve(x));
    return Flush(),0;
}
posted @ 2019-10-23 09:50  Shiina_Mashiro  阅读(137)  评论(0编辑  收藏  举报