zoukankan      html  css  js  c++  java
  • hdu4135容斥原理 组合遍历

    容斥原理实现的关键在于:组合遍历,即如何遍历2^n种组合。
    容斥原理的三种写法:

    • DFS
    • 队列数组
    • 位数组
    #include<stdio.h>
    #include<iostream>
    #include<stdlib.h>
    #include<string.h> 
    using namespace std;
    const int maxn = 32000;
    bool isPrime[maxn];
    int prime[maxn / 4], psize;//线性筛法必须用数组存储现有质数 
    int p[40], ps;
    typedef long long ll;
    void init(){
    	memset(isPrime, 1, sizeof(isPrime));
    	psize = 0;
    	for (int i = 2; i < maxn; i++){
    		if (isPrime[i]){
    			prime[psize++] = i;
    		}
    		for (int j = 0; j < psize&&prime[j] * i < maxn; j++){
    			isPrime[i*prime[j]] = false;
    			if (i%prime[j] == 0)
    				break;
    		}
    	}
    }
    void parse(ll x){
    	ps = 0;
    	for (int i = 0;i<psize; i++){
    		if (x%prime[i] == 0){
    			p[ps++] = prime[i];
    			while (x%prime[i] == 0)x /= prime[i]; 
    			if (x == 1)return;
    		}
    	}
    	if (x>1){
    		p[ps++] = x;
    	}
    }
    ll dfs(ll ind, ll n, ll x){
    	ll s = 0;
    	for (int i = ind; i < ps; i++){
    		s += n / p[i] - dfs(i + 1, n / p[i], x);
    	}
    	return s;
    }
    ll huzhi(ll n, ll x){
    	return  n - dfs(0, n, x);
    } 
    int main(){
    	freopen("in.txt", "r", stdin);
    	init(); 
    	int T; scanf("%d", &T);
    	int caseid = 1;
    	ll A, B, N;
    	while (T--){
    		cin >> A >> B >> N;
    		ll ans;
    		if (N == 1){
    			ans = B - A+1;
    		}
    		else{
    			parse(N);
    			ans = huzhi(B, N) - huzhi(A-1, N);
    		}
    		printf("Case #%d: %I64d
    ", caseid++, ans);
    	}
    	return 0;
    }
    

    队列数组法实现容斥原理:

    __int64 haha(__int64 m) //用队列数组实现容斥原理
    		{
    	__int64 que[10000], t = 0, sum = 0;
    	que[t++] = -1;
    	for (int i = 0; i < num; i++) {
    		int k = t;//保存一层
    		for (int j = 0; j < k; j++)
    			que[t++] = que[j] * a[i] * (-1);
    	}
    	for (int i = 1; i < t; i++)
    		sum = sum + m / que[i];
    	return sum;
    }
    

    队列数组实现容斥原理的思想就是:让下列元素按顺序进队列:

    • 1=1+空 ----1处理完了
    • 2=2+空
    • 12=2+1 ----2处理完了
    • 3=3+空
    • 13=3+1
    • 23=3+2
    • 123=3+12 ---3处理完了

    队列数组需要开辟一个队列来存储过去的元素,浪费空间。但是它对于每种组合,它没有从头开始计算,而是按照一定的顺序在过去基础上计算。借鉴这个思想,位数组不一定每次都需要从头计算,可以通过格雷码来表示位数组,这样每次只变化1位,不需要额外空间,兼具队列数组和位数组的优点。

    如何获取变化的那一位呢?记m为第i+1个格雷码,n为第i个格雷码,则mn即为变化的那一位,log2(mn)即得下标。
    接下来就可以在上一步的基础上进行加入或移除操作。

  • 相关阅读:
    LeetCode-求最长回文子序列
    C++四种类型转换总结
    kmp算法分析和C++实现
    把二叉树打印成多行
    考研数据结构笔记—堆排序
    天勤考研数据结构笔记—栈的C语言实现
    合并两个排序的链表递归和非递归C++实现
    二叉树的线索化
    单链表的基本操作实现
    OpenFaceswap 入门教程(3): 软件参数篇!
  • 原文地址:https://www.cnblogs.com/weiyinfu/p/6369853.html
Copyright © 2011-2022 走看看