zoukankan      html  css  js  c++  java
  • Luogu P5366 [SNOI2017]遗失的答案

    题目
    筛出(L)的每个质因子,限制条件实际上就是限制了每个质因子的出现次数([l,r]),且(l,r)都有正好取到的。
    显然(L)最多有(8)个不同的因子,所以对每个数而言,我们对其每个质因子是否取到(l)(r)状压,这样([1,n])的数就可以表示成一个(16)位的二进制数。
    那么我们要求的就是在强制选某个二进制数的情况下,选出一些二进制数使得其按位或为全集的方案数。
    (f(S))表示选出一些二进制数使得其按位或为(S)的方案数。
    考虑容斥,设(g(S)=sumlimits_{Tsubseteq S}f(T))
    那么显然的,记(cnt_S)表示有多少个二进制数是(S)的子集,(g(S)=2^{cnt_S})
    那么我们容斥即可得到(f(S)=sumlimits_{Tsubseteq S}(-1)^{|S|-|T|}g(T))
    然后考虑计算强制选择某个二进制数时的答案。
    设这个数为(T),其补集为(S)
    那么我们需要枚举的就是(S)的超集(即子集包含(S)的集合)。
    这个可以转化为枚举(S)的子集,然后枚举的超集就是(Tcup 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('
    ');}
    }
    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;
    }
    
  • 相关阅读:
    小组自评、互评及反馈
    "一个程序员的生命周期"读后感
    阅读《构建之法》第10-12章
    阅读《构建之法》第8,9,10章
    冒泡算法程序分析树的评论
    1014 C语言文法定义与C程序的推导过程
    0917 实验一词法分析程序
    0909 我与编译原理不得不说的秘密
    复利计算器4
    复利及单利计算
  • 原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/11724415.html
Copyright © 2011-2022 走看看