容斥原理

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

所以通常深搜比较好写,设置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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值