就是把每个区间的大小加到总和上,然后把每两个区间的相交部分减去,把每三个区间的相交部分加上,如此处理……
所以通常深搜比较好写,设置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相乘后再除以最大公约数,因为会发生溢出。
}