• UOJ #129 / BZOJ 4197 / 洛谷 P2150


    题面传送门

    题意:

    • 你有一个集合 (S={2,3,dots,n})
    • 你要选择两个集合 (A)(B),满足:
    1. (A subseteq S)(B subseteq S),且 (A cap B=varnothing)
    2. 不存在两个数 (x in A)(y in B),且 (operatorname{gcd}(x,y)>1)
    • 求满足条件的集合 (A,B) 的数量。
      (n in [2,500])

    通过分析题面可以发现,如果两个集合 (A,B) 满足条件,那么它们的质因子组成的集合一定不会重复。
    先考虑 (30) 分的做法。(30) 以内的质数只有 (10) 个((2,3,5,7,11,13,17,19,23,29))。
    我们分别给这 (10) 个质数标号 (1)(10)。这样我们可以使用一个 (10) 位二进制数表示质因子的选择情况。
    例如 ((1011001100)_2) 就表示集合中包含质因子 (2,5,7,17,19)
    再将 (2)(n) 每个数分解质因数。记 (fac_i)(i) 质因子的集合。
    到这里,不少题解都使用了三维状压 (dp)。这里我要介绍一个不太一样的方法。
    我们开一个数组 (cnt_{i,j}),其中 (i in [2,n],j in [0,2^{10}-1])
    表示满足 (2 leq x leq i)(fac_i subseteq j)(x) 的个数
    再设 (dp_s) 表示质因子集合为 (s) 的集合数量。
    显然所有 (fac_i subseteq s)(i) 都可以被选,那么总共有 (cnt_{n,i}) 个数,每个数都有选与不选两种可能,故 (dp_s=2^{cnt_{n,i}})
    最后是统计答案,枚举质因子集合 (s,t)(根据条件 2,(s&t=0)),两个集合的选择方法是独立的,因此,根据乘法原理,这一部分对答案的贡献为 (dp_s imes dp_t)
    但是这样做还不行。假设有一个集合 (T={2,4}),那么 (T)(dp_1) 中被计算了一次,在 (dp_3) 中又被计算了一次,会被重复计算。
    这时不难想到容斥原理。你对 (dp) 数组进行一遍容斥原理就可以了。

    再考虑正解。(n)(30) 扩大到了 (500),就不能像 (30) 分做法那样将一个质因子集合用二进制表示出来了。
    但是注意到 (sqrt{500}) 只有 (22),而 (22) 以内的质数只有 (8) 个。
    那么我们可以用根号分治的思想将质数分成两类,一类是 (leq 22) 的质数(我们称其为“小质数”),另一类是 (gt 22) 的质数(我们称其为“大质数”)。
    不难发现,(1)(500) 中的数最多只有一个大质数因子。任意两个大质数乘起来都会 (gt 500)
    换句话说,大质数的选择方案是独立的(是否选择 (31) 这个质因子不会影响 (37) 的选择方案)
    我们枚举小质数因子的集合 (s,t),然后考虑每个大质数 (p) 的选择方法:

    1. (p)(A) 中,那么选择的方案数为 (2^{cnt_{frac{n}{p},s}+1})(你可以看成从 ([1,frac{n}{p}]) 选择若干个质因子集合为 (s) 的集合然后将每个数乘以 (p)
    2. (p)(B) 中,那么选择的方案数为 (2^{cnt_{frac{n}{p},t}+1})
    3. (p) 既不在 (A) 也不在 (B) 中,方案数为 (1)
      将三者加起来就是选择 (p) 的方案数。
      将选择每个大质数的方案数乘起来就是选择大质数的方案数。
      选择小质数的方案数和 (30) 分做法一样用乘法原理将两部分乘起来。
      用容斥原理算一下就可以了。
    /*
    Contest: -
    Problem: P2150
    Author: tzc_wk
    Time: 2020.7.31
    */
    #include <bits/stdc++.h>
    using namespace std;
    #define fi			first
    #define se			second
    #define fz(i,a,b)	for(int i=a;i<=b;i++)
    #define fd(i,a,b)	for(int i=a;i>=b;i--)
    #define foreach(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
    #define all(a)		a.begin(),a.end()
    #define fill0(a)	memset(a,0,sizeof(a))
    #define fill1(a)	memset(a,-1,sizeof(a))
    #define fillbig(a)	memset(a,0x3f,sizeof(a))
    #define fillsmall(a) memset(a,0xcf,sizeof(a))
    #define y1			y1010101010101
    #define y0			y0101010101010
    #define int long long
    typedef pair<int,int> pii;
    inline int read(){
    	int x=0,neg=1;char c=getchar();
    	while(!isdigit(c)){
    		if(c=='-') neg=-1;
    		c=getchar();
    	}
    	while(isdigit(c)) x=x*10+c-'0',c=getchar();
    	return x*neg;
    }
    int n=read(),m=read();
    inline int qpow(int x,int e){
    	int ans=1;
    	while(e){
    		if(e&1)	ans=ans*x%m;
    		x=x*x%m;
    		e>>=1;
    	}
    	return ans;
    }
    int pr[505],num[505],pcnt=0;
    bool vis[505];
    inline void sieve(){
    	for(int i=2;i<=500;i++){
    		if(!vis[i]){
    			pr[++pcnt]=i;num[i]=pcnt;
    			for(int j=i+i;j<=500;j+=i) vis[j]=1;
    		}
    	}
    }
    int sumfac[505][1<<8];
    int g[505][1<<8],gg[505][1<<8];
    inline int calc_fac(int x){
    	int tmp=x,flg=0;
    	for(int i=2;i*i<=x;i++){
    		if(tmp%i==0){
    			flg|=(1<<(num[i]-1));
    			while(tmp%i==0) tmp/=i;
    		}
    	}
    	if(tmp>1){
    		if(num[tmp]>8) return 256;
    		else flg|=(1<<(num[tmp]-1));
    	}
    	return flg;
    }
    signed main(){
    	sieve();
    	fz(i,2,n){
    		int f=calc_fac(i);
    		for(int j=0;j<(1<<8);j++){
    			if((j&f)!=f) sumfac[i][j]=sumfac[i-1][j];
    			else sumfac[i][j]=sumfac[i-1][j]+1;
    //			cout<<i<<" "<<j<<" "<<sumfac[i][j]<<endl;
    		}
    	}
    	for(int i=0;i<(1<<8);i++) for(int j=0;j<(1<<8);j++){
    		if((i&j)==0){
    //			if(dp[i][n]==0||dp[j][n]==0) continue;
    //			cout<<i<<" "<<j<<" "<<dp[i][n]<<" "<<dp[j][n]<<endl;
    			int ans=1;
    			ans=ans*qpow(2,sumfac[n][i])%m;
    			ans=ans*qpow(2,sumfac[n][j])%m;
    			for(int k=23;k<=n;k++){
    				if(num[k]){
    //					if(k==23) cout<<i<<" "<<j<<" "<<qpow(2,sumfac[n/k][i]+1)+qpow(2,sumfac[n/k][j]+1)-1<<endl;
    					ans=ans*(qpow(2,sumfac[n/k][i]+1)+qpow(2,sumfac[n/k][j]+1)-1+m)%m;
    				}
    			}
    			g[i][j]=ans;
    //			cout<<i<<" "<<j<<" "<<g[i][j]<<endl;
    		}
    	}
    	for(int i=0;i<(1<<8);i++) for(int j=0;j<(1<<8);j++){
    		if((i&j)==0){
    			for(int k=0;k<(1<<8);k++){
    				if((j&k)!=k) continue;
    				gg[i][j]=(gg[i][j]+(int)pow(-1,__builtin_popcount(j^k))*g[i][k]+m)%m;
    			}
    //			cout<<i<<" "<<j<<" "<<gg[i][j]<<endl;
    		}
    	}
    	int sum=0;
    	for(int i=0;i<(1<<8);i++) for(int j=0;j<(1<<8);j++){
    		if((i&j)==0){
    			int x=0;
    			for(int k=0;k<(1<<8);k++){
    				if((i&k)!=k) continue;
    				x=(x+(int)pow(-1,__builtin_popcount(i^k))*gg[k][j]+m)%m;
    			}
    			sum=(sum+x)%m;
    //			cout<<i<<" "<<j<<" "<<x<<endl;
    		}
    	}
    	cout<<sum<<endl;
    	return 0;
    }
    
  • 相关阅读:
    csrf攻击实例
    《四 数据库连接池源码》手写数据库连接池
    《四 spring源码》手写springmvc
    spring和springmvc是单例还是多例
    redis集群设置密码
    mongodb3.6集群搭建:分片集群认证
    mongodb3.6集群搭建:分片+副本集
    Mongo 3.6.1版本Sharding集群配置
    windows计划任务
    Redis slowlog慢查询
  • 原文地址:https://www.cnblogs.com/ET2006/p/13417827.html
Copyright © 2020-2023  润新知