容斥原理

来源:互联网 发布:java防止重复提交 编辑:程序博客网 时间:2024/04/30 15:18

就是把每个区间的大小加到总和上,然后把每两个区间的相交部分减去,把每三个区间的相交部分加上,如此处理……

所以通常深搜比较好写,设置falg奇数时加上当前值,偶数时减去当前值,重点就是如何让求每一轮的值,注意不要重复,

深搜方向的选择,或者状压存状态,如此,如此……

eg:

HDU1796 How many integers can you find

http://acm.hdu.edu.cn/showproblem.php?pid=1796

题意:

求小于n的数中能够整除a数组中某个数的数有多少个。

思路:

可以简单求出数组中每个数在小于n的数中的答案个数,就是(n-1)/a[i],之后相加就是全部的数,但是有重复,所以要减去同时是两个数的倍数的数,

eg:a[ ] = {2,3}; sum=(n-1)/2+(n-1)/3-(n-1)/(lcm(2,3));

然而当a数组中的数多余两个时,减去的数中也会有重复,所以再加上同时是三个数的倍数的数,加上数中也会存在重复,所以需要减去四个数的倍数的数,如此……就是容斥原理了。

解题代码:

#include<iostream>#include<cstdio>using namespace std;int a[21];int n,m;long long summ;int gcd(int x,int y){    return y?gcd(y,x%y):x;}int lcm(int x,int y){    return x/gcd(x,y)*y;}void dfs(int now,int step,int flag){    summ+=(n/step)*flag;    for(int i=now+1;i<m;i++)        dfs(i,lcm(max(step,a[i]),min(step,a[i])),-flag);}int main(){    int i,j;    while(scanf("%d%d",&n,&m)!=EOF&&n)    {        n--;        for(i=0,j=0;i<m;i++)        {            scanf("%d",&a[i]);            if(a[i])                a[j++]=a[i];        }        m=m-(i-j);        summ=0;        for(i=0;i<m;i++)            dfs(i,a[i],1);        printf("%lld\n",summ);    }    return 0;}
注意:
最小公倍数的求解函数,

int lcm(int x,int y)
{
    return x/gcd(x,y)*y;  //不能将y直接与x相乘后再除以最大公约数,因为会发生溢出。
}
1 0
原创粉丝点击