bzoj5019: [Snoi2017]遗失的答案

我觉得可能很多人的做法都很傻逼 这毒瘤路牌好好的把我都搞懵逼了

首先先考虑质因数分解G、L,选出的数中质因子的最小次幂和最大次幂要分别等于G、L对应质因子的次幂

可以发现L的质因子个数不会超过8个,n之内满足条件的数的个数不会超过800,考虑状压DP

当没有必选x的限制时,f[i][j][k]表示前i个合法的数,每个质因数下界是否满足,上界是否满足。预处理每个合法的数是否顶上下界,转移显然,实际上第一维可以省去,我做的时候直接把后两维并一起了

 

现在思考询问,对于固定x要选如何快速得到,想法是直接预处理所有x的答案。

考虑补集转化,求不带x的方案数

有一种非常毒瘤的做法就是分别出来前缀和后缀,询问的时候直接FWT做或卷积,预处理的空间又大跑得又龟除了完全不动脑没有什么优点

不过看到了一种很有意思很优美的分治做法,先进行[l,mid]的影响,然后去递归得到[mid+1,r]的答案,做完以后通过记录上一层的答案重新进行当前层,进行[mid+1,r]的影响,然后递归得到[l,mid]的答案,这个东西理论的时空复杂度应该是O(len*loglen*(2^plen)^2)略小于FWT

 

我自己的做法是这样的:(不用补集转化)考虑对于必选x,把它的对应的状态用zt表示,那么现在要求的是li^zt的每个超集不包含x的方案数。不难发现,除非是满集,否则li^zt的超集一定不包含x,这里先预处理。考虑如何把满集的贡献计算。只需要去掉必须用x的方案,那么x是可有可无的,直接减除二就可以了。

形式化的:li^zt的每个超集不包含x的方案数 = 超集中非满集不包含x的方案数 + 满集不包含x的方案数

= 超集中非满集的方案数 + (满集的方案数 - 必须用x的方案数)/2

= 超集中非满集的方案数 + (满集的方案数 - 超集中非满集的方案数)/2

枚举子集统计一下超集和(不含满集)就完了

至于网上算至少然后带个容斥系数的,您就图个乐子看吧

 

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<map>

#define ft first
#define sd second
using namespace std;
const int _=1e2;
const int taxp=8;
const int maxp=taxp+2;
const int maxz=(1<<2*taxp)+_;
const int maxn=8e2+10;
typedef long long LL;
typedef pair<int,int> pp;
const int mod=1e9+7;
inline int ad(int x,int y){return (x>=mod-y)?x-mod+y:x+y;}
inline int re(int x,int y){return (x<y)?x-y+mod:x-y;};
int n,G,L;

int plen,p[maxp],q[2*maxp];
void divi()
{
    plen=0; int K=L;
    for(int i=2;i*i<=K;i++)
        if(K%i==0){p[++plen]=i;while(K%i==0)K/=i;}
    if(K!=1)p[++plen]=K;
    sort(p+1,p+plen+1);
        
    K=L;
    for(int i=1;i<=plen;i++)
        if(K%p[i]==0){while(K%p[i]==0)K/=p[i],q[i+plen]++;}
    K=G;
    for(int i=1;i<=plen;i++)
        if(K%p[i]==0){while(K%p[i]==0)K/=p[i],q[i]++;}
}
int len;pp num[maxn]; map<int,int>leg;
void getnum(int k,int d,int s)
{
    if(k==plen+1){num[++len]=make_pair(d,s);return ;}
    for(int i=q[k],g=int(pow(p[k],q[k])); i<=q[plen+k]; i++,g*=p[k])
        if((LL)d*g<=n)getnum(k+1,d*g, s | ((i==q[k])?(1<<k-1):0) | ((i==q[plen+k])?(1<<plen+k-1):0) );
}

int f[maxz],d[maxz];
void DP()
{
    f[0]=1; int li=(1<<plen*2)-1;
    for(int i=0;i<len;i++)
        for(int zt=li;zt>=0;zt--)
            if(f[zt])f[zt|num[i+1].sd]=ad(f[zt|num[i+1].sd],f[zt]);
    
    for(int zt=0;zt<li;zt++)
        if(f[zt])
        {
            for(int ut=zt;ut;ut=(ut-1)&zt)d[ut]=ad(d[ut],f[zt]);
            d[0]=ad(d[0],f[zt]);
        }
}

int main()
{
    scanf("%d%d%d",&n,&G,&L);
    if(L%G==0)
    {
        divi();
        getnum(1,1,0);
        sort(num+1,num+len+1);
        for(int i=1;i<=len;i++)leg[num[i].ft]=i;
        DP();
    }
    
    int Q,x;
    scanf("%d",&Q); int li=(1<<plen*2)-1;
    while(Q--)
    {
        scanf("%d",&x);    if(L%G!=0){puts("0");continue;}
        if(!leg.count(x)){puts("0");}
        else
        {
            int D=d[li^num[leg[x]].sd];
            printf("%d\n",ad((LL)re(f[li],D)*(mod/2+1)%mod,D));
        }
    }
    
    return 0;
}
View Code

 

 

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<map>

#define ft first
#define sd second
using namespace std;
const int _=1e2;
const int taxp=8;
const int maxp=taxp+2;
const int maxz=(1<<2*taxp)+_;
const int maxn=8e2+10;
typedef long long LL;
typedef pair<int,int> pp;
const int mod=1e9+7;
inline int ad(int x,int y){return (x>=mod-y)?x-mod+y:x+y;}
inline int re(int x,int y){return (x<y)?x-y+mod:x-y;};
int n,G,L;

int plen,p[maxp],q[2*maxp];
void divi()
{
    plen=0; int K=L;
    for(int i=2;i*i<=K;i++)
        if(K%i==0){p[++plen]=i;while(K%i==0)K/=i;}
    if(K!=1)p[++plen]=K;
    sort(p+1,p+plen+1);
        
    K=L;
    for(int i=1;i<=plen;i++)
        if(K%p[i]==0){while(K%p[i]==0)K/=p[i],q[i+plen]++;}
    K=G;
    for(int i=1;i<=plen;i++)
        if(K%p[i]==0){while(K%p[i]==0)K/=p[i],q[i]++;}
}
int len;pp num[maxn]; map<int,int>leg;
void getnum(int k,int d,int s)
{
    if(k==plen+1){num[++len]=make_pair(d,s);return ;}
    for(int i=q[k],g=int(pow(p[k],q[k])); i<=q[plen+k]; i++,g*=p[k])
        if((LL)d*g<=n)getnum(k+1,d*g, s | ((i==q[k])?(1<<k-1):0) | ((i==q[plen+k])?(1<<plen+k-1):0) );
}

int li,f[30][maxz],sum,as[maxn];
void flu(int *a,int i)
{
    for(int zt=li;zt>=0;zt--)
        if(a[zt])a[zt|num[i].sd]=ad(a[zt|num[i].sd],a[zt]);
}
void dc(int k,int l,int r)
{
    if(l==r)
    {
        if(l==len)
        {
            memcpy(f[k],f[k-1],sizeof(f[k]));
            flu(f[k],l); sum=f[k][li];
        }
        as[l]=re(sum,f[k-1][li]);
        return ;
    }
    int mid=(l+r)/2;
    
    memcpy(f[k],f[k-1],sizeof(f[k]));
    for(int i=l;i<=mid;i++)flu(f[k],i);
    dc(k+1,mid+1,r);
    
    memcpy(f[k],f[k-1],sizeof(f[k]));
    for(int i=mid+1;i<=r;i++)flu(f[k],i);
    dc(k+1,l,mid);
}

int main()
{
    scanf("%d%d%d",&n,&G,&L);
    if(L%G==0)
    {
        divi();
        getnum(1,1,0);
        sort(num+1,num+len+1);
        for(int i=1;i<=len;i++)leg[num[i].ft]=i;
        li=(1<<plen*2)-1;
        f[0][0]=1;dc(1,1,len);
    }
    
    int Q,x;
    scanf("%d",&Q);
    while(Q--)
    {
        scanf("%d",&x);    if(L%G!=0){puts("0");continue;}
        if(!leg.count(x)){puts("0");}
        else printf("%d\n",as[leg[x]]);
    }
    
    return 0;
}
分治

 

 

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<map>

#define ft first
#define sd second
using namespace std;
const int _=1e2;
const int taxp=8;
const int maxp=taxp+2;
const int maxz=(1<<2*taxp)+_;
const int maxn=8e2+10;
typedef long long LL;
typedef pair<int,int> pp;
const int mod=1e9+7;
inline int ad(int x,int y){return (x>=mod-y)?x-mod+y:x+y;}
inline int re(int x,int y){return (x<y)?x-y+mod:x-y;};
int n,G,L;

int plen,p[maxp],q[2*maxp];
void divi()
{
    plen=0; int K=L;
    for(int i=2;i*i<=K;i++)
        if(K%i==0){p[++plen]=i;while(K%i==0)K/=i;}
    if(K!=1)p[++plen]=K;
    sort(p+1,p+plen+1);
        
    K=L;
    for(int i=1;i<=plen;i++)
        if(K%p[i]==0){while(K%p[i]==0)K/=p[i],q[i+plen]++;}
    K=G;
    for(int i=1;i<=plen;i++)
        if(K%p[i]==0){while(K%p[i]==0)K/=p[i],q[i]++;}
}
int len;pp num[maxn]; map<int,int>leg;
void getnum(int k,int d,int s)
{
    if(k==plen+1)
    {
        num[++len]=make_pair(d,s);
//        printf("%d %d\n",d,s);
        return ;
    }
    for(int i=q[k],g=int(pow(p[k],q[k])); i<=q[plen+k]; i++,g*=p[k])
        if((LL)d*g<=n)getnum(k+1,d*g, s | ((i==q[k])?(1<<k-1):0) | ((i==q[plen+k])?(1<<plen+k-1):0) );
}

void FWT(int *a,int n,int op)
{
    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(op==1)a[j+k+i]=ad(a[j+k+i],a[j+k]);
                else a[j+k+i]=re(a[j+k+i],a[j+k]);
}
int f[maxn][maxz],g[maxn][maxz],h[maxz],d[maxn];
void DP()
{
    f[0][0]=1; int li=(1<<plen*2)-1;
    for(int i=0;i<len;i++)
        for(int j=0;j<=li;j++)
            if(f[i][j])
            {
                f[i+1][j|num[i+1].sd]=ad(f[i+1][j|num[i+1].sd],f[i][j]);
                f[i+1][j]=ad(f[i+1][j],f[i][j]);
            }
    
    g[len+1][0]=1;
    for(int i=len+1;i>1;i--)
        for(int j=0;j<=li;j++)
            if(g[i][j])
            {
                g[i-1][j|num[i-1].sd]=ad(g[i-1][j|num[i-1].sd],g[i][j]);
                g[i-1][j]=ad(g[i-1][j],g[i][j]);
            }
    
    d[1]=g[2][li],d[len]=f[len-1][li];
    for(int i=2;i<len;i++)
    {
        FWT(f[i-1],li+1,1),FWT(g[i+1],li+1,1);
        for(int j=0;j<=li;j++)h[j]=(LL)f[i-1][j]*g[i+1][j]%mod;
        FWT(h,li+1,-1);
        d[i]=h[li];
    }
}

int main()
{
    scanf("%d%d%d",&n,&G,&L);
    if(L%G==0)
    {
        divi();
        getnum(1,1,0);
        sort(num+1,num+len+1);
        for(int i=1;i<=len;i++)leg[num[i].ft]=i;
        DP();
    }
    
    int Q,x;
    scanf("%d",&Q); int li=(1<<plen*2)-1;
    while(Q--)
    {
        scanf("%d",&x);    if(L%G!=0){puts("0");continue;}
        if(!leg.count(x)){puts("0");}
        else printf("%d\n",re(f[len][li],d[leg[x]]));
    }
    
    return 0;
}
FWT

 

posted @ 2019-04-18 11:19  AKCqhzdy  阅读(230)  评论(0编辑  收藏  举报