12.10周日

来源:互联网 发布:telnet 端口 编辑:程序博客网 时间:2024/06/12 02:11

容斥原理:要计算几个集合并集的大小,先将所有单个集合的大小相加,然后减去所有两个集合相交的部分,再加回所有三个集合相交的部分,再减去所有四个集合相交的部分,依此类推,一直计算到所有集合相交的部分。

关于集合的原理公式 

      

 

     即为

        

         

下面以poj2773为例,来整理对于容斥原理的编程实现


Happy 2006
Time Limit: 3000MS
Memory Limit: 65536K
Total Submissions: 10818
Accepted: 3760
Description
Two positive integers are said to be relatively prime to each other if the Great Common Divisor (GCD) is 1. For instance, 1, 3, 5, 7, 9...are all relatively prime to 2006.


Now your job is easy: for the given integer m, find the K-th element which is relatively prime to m when these elements are sorted in ascending order.
Input
The input contains multiple test cases. For each test case, it contains two integers m (1 <= m <= 1000000), K (1 <= K <= 100000000).
Output
Output the K-th element in a single line.
Sample Input
2006 1
2006 2
2006 3
Sample Output
1
3


1.可以由欧几里德算法来做。

如果a与b互素,则b×t+a与b一定互素,即与m互素的数对m取模具有周期性,周期为t。


#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <iostream>
using namespace std;


typedef long long LL;
const int maxn = 100000 + 10;
int m, k;
int s[maxn];  


int gcd(int a,int b)  
{  
    if(b==0)  
    {  
        return a;  
    }  
    else  
    {  
        return gcd(b,a%b);  
    }  
}  


int main()  
{  
    while(scanf("%d%d",&m,&k)!=EOF)  
    {  
        int i;  
        int num=0;  
        for(i=1;i<=m;i++)     
        {  
            if(gcd(m,i)==1)  
            {  
                s[num++]=i;  
            }  
        }  
        if(k%num==0)  //恰好为整数个周期
        {  
            printf("%d\n",(k/num-1)*m + s[num-1]); // [k/num]后需要-1
        }  
        else  
        {  
            printf("%d\n",k/num*m + s[k%num-1]);  
        }  
    }  
    return 0;  
}  


2.考虑问题的逆问题,运用容斥原理。

m = p1^a1 * p2^a2 * ... * pi^ai, 那么区间[1,r]中,与m不互质的数中会有p1 p2 .. pi这些因子,可以求出区间里与m不互质的数的个数,用区间总的长度减去它们的个数,即为区间中与m互质的数的个数。 假设Ai是含有因子Pi的数的个数,那么答案就是 r - (A1+A2+ .. Ai) + (Ai并Aj i!=j) -  ... Ai 。求出个数后,就可以用二分来快速求解。

实际上,区间[1,r]中,含有因子p的数的个数即为 r/p。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <iostream>
using namespace std;


typedef long long LL;
const int maxn = 100000 + 10;
int m, k;
int pi[maxn], npi;//存放某个数的素因子,素因子个数
bool vis[maxn];
int prime[maxn], num;//存放素数,素数个数


void shai(int n)//筛素数
{
    memset(vis, 0, sizeof(vis));
    int m = sqrt(n+0.5);
    for(int i=2; i<=m; i++) if(!vis[i])
        for(int j=i*i; j<=n; j+=i) vis[j] = 1;
    num = 0;
    for(int i=2; i<=n; i++) if(vis[i] == 0)
        prime[num++] = i;
}


LL check(LL r)   //计算区间[1,r]中与m互质的数的个数
{
    LL res = r;
    for(int i=1; i<(1<<npi); i++)

//1<<npi   npi为数的素因子的总个数。设想有一段长为npi的序列,这段序列以二进制数0,1组成,那么可以用此序列表示    素因子的所有不同组合情况。可知,总数即为2的(npi)次幂,用位运算符表示即为(1<<npi)。
    {
        int bits=0, sum=1;
        for(int j=0; j<npi; j++)
            if(((i>>j)&1)==1) bits++, sum*=pi[j];
        if(bits%2==1) res -= r/sum;
        else res += r/sum;
    }
    return res;
}


int main()
{
    shai(100000);

    while(cin>>m>>k)
    {
        npi = 0;
        int tp = m;
        for(int i=0; i<num; i++)
        {
            if(tp%prime[i] == 0)
            {
                pi[npi++] = prime[i];
                while(tp%prime[i]==0) tp/=prime[i];
            }
            if(tp == 1) break;//及时跳出循环
        }
        if(tp != 1) pi[npi++] = tp;//tp为数的素因子


        LL l=1, r=0x3f3f3f3f3f3f3f3f;//二分快速求解
        LL res = 1;
        while(l <= r)
        {
            LL mid = (l+r)/2;
            if(check(mid)>=k)
            {
                res = mid;
                r = mid-1;
            }
            else l = mid + 1;
        }
        cout<<res<<endl;
    }
    return 0;
}

原创粉丝点击