习题:遗失的答案(FWT)

题目

传送门

思路

很明显,当\(G \not| L\)的时候无解,

先对最大公因数的条件进行化简,可以将所有的值都除以\(G\),只需要选出来的数互质即可

那么将\(l\)进行质因数分解,有\(l=\prod p_i^{a_i}\)

设选出来的第\(i\)个数可以分解成为\(s_i=\prod p_{i}^{b_{ij}}\)

那么有\(a_i=max\{b_{ij}\}\),可以发现,如果一个位置上已经满足,剩下的可以任意选择

对于\(1e8\)之内的数据,每一个数最多有\(8\)个质因数,也就是指需要\(16\)个位置来进行限制

设$dp[s] \(表示是否满足第\)i$个质因数的条件

对每一个数都处理一下,令\(A_i\)为转换第\(i\)个数之后得到的集合

当然,所转换的数必须是\(L/G\)的因数

那么有\(dp[s]=\sum_{A|B=s} dp[A]dp[B]\)

如果没有\(X\)的限制,这道题就已经解决了,

如果有\(X\)的限制,可以考虑先没有\(X\)将所有的\(dp\)处理出来,在将\(X\)加进去

可以发现,有一些数的\(A\)是相同的,我们将这些\(A\)相同的分到一组里面去,

经过打表,可以发现这样的组不会有很多,大概在\(600\)左右

\(f[i][j]\)表示前\(i\)组已经达到的状态为\(j\)的方案数,\(g[i][j]\)表示后\(i\)组到达状态\(j\)的方案数,

\(X\)所在的组为\(index\),那么我们所需要做的就是将\(f[index-1][j]\)\(g[index+1][j]\)卷起来

之后将\(X\)插入即可,可以发现,如果这样去思考,\(Q=1e5\)实际上和\(Q=600\)是没有区别的

预处理一下单独除去每一组的答案即可,

预处理的复杂度为\(O(600*2^{16}*16)\),询问的复杂度为\(O(Qlog_n)\)

但是如果仔细思考这个过程,将组的顺序合理安排一下,也就是从小到大,那么可以多出很多的空数组,常数也许会比较小。。。

代码

#pragma GCC optimize(2)
#include<unordered_map>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<cstdio>
using namespace std;
namespace IO
{
    template<typename T> void read(T &x)
    {
        x=0;
        int f=1;
        char c=getchar();
        while('0'>c||c>'9')
        {
            if(c=='-')  
                f=-1;
            c=getchar();
        }
        while('0'<=c&&c<='9')
        {
            x=(x<<3)+(x<<1)+c-'0';
            c=getchar();
        }
        x*=f;
    }
    template<typename T> void write(T x)
    {
        if(x>9)
            write(x/10);
        putchar(x%10+'0');
    }
}
namespace FWT
{
    const int mod=1e9+7;
    void fwt(int *a,int l,int r)
    {
        if(l==r)
            return;
        int mid=(l+r)>>1;
        for(int i=l;i<=mid;i++)
            a[i-l+1+mid]=(a[i-l+1+mid]+a[i])%mod;
        fwt(a,l,mid);
        fwt(a,mid+1,r);
    }
    void idfwt(int *a,int l,int r)
    {
        if(l==r)
            return;
        int mid=(l+r)>>1;
        idfwt(a,l,mid);
        idfwt(a,mid+1,r);
        for(int i=l;i<=mid;i++)
            a[i-l+1+mid]=(a[i-l+1+mid]-a[i]+mod)%mod;
    }
}
using namespace FWT;
using namespace IO;
int n,g,l,q;
int num[605],st[605],len;//每一组的个数&每一组的状态&有多少个组
int pre[605][(1<<16)+5],suf[605][(1<<16)+5];
int ans[605];//强制选第i组中的某个数的结果
int temp[605][(1<<16)+5]={};
int lenp,lim;
pair<int,int> p[15];
vector<int> fac;
unordered_map<int,int> vis;
int getsta(int x)
{
    int ret=0;
    for(int i=0;i<lenp;i++)
    {
        int tot=0;
        while(x%p[i].first==0)
        {
            x/=p[i].first;
            tot++;
        }
        if(tot==0)
            ret|=(1<<i);
        if(tot==p[i].second)
            ret|=(1<<(i+lenp));
    }
    return ret;
}
long long qkpow(int a,int b)
{
    int t=1;
    int mul=a;
    while(b)
    {
        if(b&1)
            t=t*mul%mod;
        mul=mul*mul%mod;
        b>>=1;
    }
    return t;
}
void prepa()
{
    pre[0][0]=1;
    suf[len+1][0]=1;
    int val1,val2;
    for(int i=1;i<=len;i++)
    {
        val1=qkpow(2,num[i])-1;
        val2=qkpow(2,num[len-i+1])-1;
        for(int j=0;j<lim;j++)
        {
            pre[i][j]=(pre[i][j]+pre[i-1][j])%mod;
            pre[i][j|st[i]]=(pre[i][j|st[i]]+1ll*pre[i-1][j]*val1%mod)%mod;
            suf[len-i+1][j]=(suf[len-i+1][j]+suf[len-i+2][j])%mod;
            suf[len-i+1][j|st[len-i+1]]=(suf[len-i+1][j|st[len-i+1]]+1ll*suf[len-i+2][j]*val2%mod)%mod;
        }
    }
    fwt(pre[0],0,lim-1);
    fwt(suf[len+1],0,lim-1);
    for(int i=1;i<=len;i++)
    {
        fwt(pre[i],0,lim-1);
        fwt(suf[i],0,lim-1);
    }
    for(int i=1;i<=len;i++)
    {
        for(int j=0;j<lim;j++)
            if((j|st[i])==j)
                temp[i][j]=1ll*pre[i-1][j]*suf[i+1][j]%mod;
            else
                temp[i][j]=0;
        idfwt(temp[i],0,lim-1);
        ans[i]=1ll*temp[i][lim-1]*qkpow(2,num[i]-1)%mod;
    }
}
int main()
{
	//freopen("missedans6.in","r",stdin);
    //freopen("my.out","w",stdout);
    read(n);read(g);read(l);
    read(q);
    if(l%g!=0)
    {
        for(int i=1;i<=q;i++)
        {
            write(0);
            putchar('\n');
        }
        return 0;
    }
    l/=g;n/=g;
    int templ=l;
    for(int i=2;i<=templ;i++)
    {
        if(templ%i==0)
        {
            p[lenp].first=i;
            while(templ%i==0)
			{
				templ/=i;
                p[lenp].second++;
        	} 
        	lenp++;
		}
    }
    /*cout<<"p:";
    for(int i=0;i<lenp;i++)
        cout<<p[i].first<<' ';
    cout<<'\n';*/
    for(int i=1;1ll*i*i<=l&&i<=n;i++)
    {
        if(l%i==0)
        {
            fac.push_back(i);
            if(i*i!=l&&l/i<=n)
                fac.push_back(l/i);
        }
    }
    //sort(fac.begin(),fac.end(),cmp);
    for(int i=0;i<fac.size();i++)
    {
        int s=getsta(fac[i]);
        //cout<<fac[i]<<"->"<<s<<'\n';
        if(vis.count(s)==0)
        {
            vis[s]=++len;
            st[len]=s;
        }
        num[vis[s]]++;
    }
    lim=(1<<(2*lenp));
    prepa();
    for(int i=1,x;i<=q;i++)
    {
        cin>>x;
        if(x%g!=0)
            write(0);
        else
        {
            x/=g;
            if(l%x!=0||x>n)
                write(0);
            else
                write(ans[vis[getsta(x)]]);
        }
        putchar('\n');
    }
    return 0;
}
/*
962 2 4050
94470
405
5
18
*/
posted @ 2021-02-04 10:22  loney_s  阅读(86)  评论(0)    收藏  举报