码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj5019: [Snoi2017]遗失的答案

时间:2017-09-10 18:48:06      阅读:201      评论:0      收藏:0      [点我收藏+]

标签:答案   memcpy   str   ons   一个   最小   公倍数   oid   get   

Description

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

Input

第一行,三个数字N,G,L,如题意所示。
第二行,一个数字Q,表示询问个数。
第三行,Q个数字,表示每个询问所问的X。
N,G,L≤10^8,Q≤10^5,1≤X≤10^8

Output

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

如果L%G非零则无解,将L的每个质因子看作一维,只有每维都取到最小(L中对应质数的次数)和最大幂次(G中对应质数的次数),才能得到对应的G和L,状压表示每一维的质因数次数是否取到最小/大值,可以选的数一定每维幂次在最小值和最大值之间,这样的数很少,在数据范围内不超过800个,分治dp得出某个数不选的方案数,用总方案数减去即得到某个数必选的方案数。时间复杂度为O((L的约数个数)*log(L的约数个数)*4^(L的不同质因子个数))

#include<bits/stdc++.h>
typedef int mat[259][259];
const int P=1e9+7;
int _(){
    int x=0,c=getchar();
    while(c<48)c=getchar();
    while(c>47)x=x*10+c-48,c=getchar();
    return x;
}
int n,g,l,qp,mx,fs[107],tg[107],tl[107],fp=0,ans[807];
inline void inc(int&a,int b){a+=b-P,a+=a>>31&P;}
struct dat{
    int v,s1,s2;
    bool operator<(const dat&w)const{return v<w.v;}
    void trans(mat a){
        for(int i=mx-1;i>=0;--i){
            int*m1=a[i],*m2=a[i|s1];
            for(int j=mx-1;j>=0;--j)inc(m2[j|s2],m1[j]);
        }
    }
}vs[807];
int vp=0;
void f1(int x){
    for(int i=2;i*i<=x;++i)if(x%i==0){
        do x/=i;while(x%i==0);
        fs[fp++]=i;
    }
    if(x>1)fs[fp++]=x;
}
void f2(int x,int*t){
    for(int i=0;i<fp;++i)for(;x%fs[i]==0;x/=fs[i],++t[i]);
}
void dfs(int w,int v,int s1,int s2){
    if(w==fp){
        if(v<=n)vs[++vp]=(dat){v,s1,s2};
        return;
    }
    for(int i=0;i<=tl[w];++i,v*=fs[w]){
        if(i>=tg[w])dfs(w+1,v,s1|(i==tg[w])<<w,s2|(i==tl[w])<<w);
    }
}
mat F[27];
int Fp=0,all;
void cpy(){
    for(int i=0;i<mx;++i)memcpy(F[Fp+1][i],F[Fp][i],sizeof(int)*mx);
    ++Fp;
}
void solve(int L,int R){
    if(L==R){
        if(R==1){
            cpy();
            vs[1].trans(F[Fp]);
            all=F[Fp][mx-1][mx-1];
            --Fp;
        }
        ans[L]=((all-F[Fp][mx-1][mx-1])%P+P)%P;
        return;
    }
    int M=L+R>>1;
    cpy();
    for(int i=M+1;i<=R;++i)vs[i].trans(F[Fp]);
    solve(L,M);
    --Fp;
    cpy();
    for(int i=L;i<=M;++i)vs[i].trans(F[Fp]);
    solve(M+1,R);
    --Fp;
}
void nos(){
    while(qp--)puts("0");
    exit(0);
}
int main(){
    n=_(),g=_(),l=_(),qp=_();
    f1(g),f1(l);
    std::sort(fs,fs+fp);
    fp=std::unique(fs,fs+fp)-fs;
    f2(g,tg),f2(l,tl);
    if(n<g)nos();
    for(int i=0;i<fp;++i)if(tg[i]>tl[i])nos();
    dfs(0,1,0,0);
    std::sort(vs+1,vs+vp+1);
    mx=1<<fp;
    F[0][0][0]=1;
    solve(1,vp);
    while(qp--){
        int x=_(),w=std::lower_bound(vs+1,vs+vp+1,(dat){x})-vs;
        if(w<=vp&&vs[w].v==x)printf("%d\n",ans[w]);
        else puts("0");
    }
    return 0;
}

 

bzoj5019: [Snoi2017]遗失的答案

标签:答案   memcpy   str   ons   一个   最小   公倍数   oid   get   

原文地址:http://www.cnblogs.com/ccz181078/p/7501689.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!