zoukankan      html  css  js  c++  java
  • NOIP模拟 乘积

    题目大意:

    给出n和k,求从小于等于n的数中取出不超过k个,其乘积是无平方因子数的方案数。无平方因子数:不能被质数的平方整除。

    题目分析:

    10(枚举(nle8)),40(简单状压(nle16)),70(高级状压(nle30)),100(正解状压nle500,kle500)。
    对于前百分之70,由于(nle30),质数只有10个,直接状压水。

    正解(状压dp+分组背包):
    注意到1~n中每个数含有的大于(sqrt{n})的质因数最多有1种,而(sqrt{n})内的质数只有8个,于是可以分别对待:
    将1~n的数进行分组,含有大于(sqrt{n})的数分为一组,不含的单独成组,组内的每个元素用8位二进制表示该数含有的小于等于(sqrt{n})的质因子。
    这样分组是因为小于等于(sqrt{n})的质数只有8个可以方便的进行状压dp,而含有大于(sqrt{n})质因子(只有1种)的数无法进行状压(时空不够),于是将其放在一个组内,dp时只取出该组中的一个数(保证大于(sqrt{n})的质因子不平方),从而降低时空。

    code:

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 550, Mod = 1e9+7;
    int T, n, k, dp[N][(1<<8)+50], state[N], val[N];
    const int prime[10] = {2, 3, 5, 7, 11, 13, 17, 19};
    vector<int> vec[N];
    int main(){
    	scanf("%d", &T);
    	while(T--){
    		scanf("%d%d", &n, &k);
    		for(register int i = 1; i <= n; i++) vec[i].clear(), state[i] = 0;
    		for(register int i = 1; i <= n; i++){
    			val[i] = i;
    			for(register int j = 0; j < 8; j++){
    				if(val[i] % prime[j] == 0){
    					val[i] /= prime[j];
    					if(val[i] % prime[j] == 0){ 
    						state[i] = -1;  //能够整除质数平方不合法
    					}
    					else state[i] |= (1 << j);
    				}
    			}
    		}
    		for(register int i = 1; i <= n; i++){
    			if(state[i] == -1) continue;
    			if(val[i] == 1){
    				vec[i].push_back(i);  //不包含大于sqrtn的质因数 
    			}
    			else vec[val[i]].push_back(i);  //大于等于sqrtn的质因数只可能有一个 
    		}
    		for(register int i = 1; i <= n; i++)
    			for(register int j = 0; j < 256; j++)
    				dp[i][j] = 0;
    		dp[0][0] = 1;
    		for(register int i = 1; i <= n; i++){
    			int vecSze = vec[i].size();
    			static int tmp[N];
    			for(register int j = 0; j < vecSze; j++) tmp[j] = state[vec[i][j]];  //卡常操作
    			for(register int l = k; l >= 1; l--){                //一组之中只能选择一个状态,l倒序 
    				for(register int j = 0; j < vecSze; j++){                            
    					for(register int sta = (255^tmp[j]); ; sta = (255^tmp[j]) & (sta - 1)){  //枚举补集,减少一些赘余的状态 
    						dp[l][sta | tmp[j]] = (dp[l][sta | tmp[j]] + dp[l-1][sta]) % Mod;
    						if(!sta) break;
    					} 
    				} 
    			} 
    		}
    		int ans = 0;
    		for(register int i = 0; i < 256; i++){
    			for(register int j = 1; j <= k; j++)
    				ans = (ans + dp[j][i]) % Mod;
    		}
    		printf("%d
    ", ans);
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    拓扑排序
    Frame Stacking 框架堆叠
    第二课 欧几里德算法与扩展欧几里德算法
    欧拉回路
    第一课 快速幂取模
    cookie使用汇总 c设置ookie的生命周期
    .net ArrayList的用法简介
    关于C#的partial修饰符
    sql server修改表结构的sql语句
    Web MVC模式中的基本表单提交
  • 原文地址:https://www.cnblogs.com/CzYoL/p/7725083.html
Copyright © 2011-2022 走看看