容斥原理
HDU 4135
Problem Description
Input
Output
Sample Input
Sample Output
题目要求
找出区间[a,b]内与n互质的个数
参考AC代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| using namespace std; LL ans; int a[1000]; int num; void get_prime(LL n) //快速求出所有n的质因子 { //使用唯一分解定理 任何一个数都可以拆分成若干个质数的乘积 for(int i=2;i*i<=n;i++) { if(n%i==0) { a[num++]=i; while(n%i==0) n/=i; } } if(n>1) //注意最后一项的处理 a[num++]=n; } LL rongchi(LL m) //容斥原理公式 { LL que[10000]; LL sum=0; int t=0; que[t++]=-1; for(int i=0;i<num;i++) { int k=t; for(int j=0;j<k;j++) que[t++]=(-1)*a[i]*que[j]; } for(int i=1;i<t;i++) sum+=m/que[i]; return sum; } int main() { /*freopen("input.txt","r",stdin);*/ int t; scanf("%d",&t); int flag=1; while(t--) { LL x,y; int n; scanf("%lld%lld%d",&x,&y,&n); memset(a,0,sizeof(a)); ans=0,num=0; get_prime(n); ans=(y-rongchi(y))-(x-1-rongchi(x-1)); printf("Case #%d: %lld\n",flag++,ans); } return 0; }
|
思路
我们可以先转化下:用(1,b)区间与n互质的数的个数减去(1,a-1)区间与n互质的数的个数,那么现在就转化成求(1,m)区间于n互质的数的个数,如果要求的是(1,n)区间
与n互质的数的个数的话,我们直接求出n的欧拉函数值即可,可是这里是行不通的!我们不妨换一种思路:就是求出(1,m)区间与n不互质的数的个数,假设为num,
那么我们的答案就是:m-num!现在的关键就是:怎样用一种最快的方法求出(1,m)区间与n不互质的数的个数?方法实现:我们先求出n的质因子(因为任何一个数都可以
分解成若干个质数相乘的),如何尽快地求出n的质因子呢?使用代码给出的方法
举一组实例吧:假设m=12,n=30.
第一步:求出n的质因子:2,3,5;
第二步:(1,m)中是n的因子的倍数当然就不互质了(2,4,6,8,10)->n/2 6个,(3,6,9,12)->n/3 4个,(5,10)->n/5 2个。
如果是粗心的同学就把它们全部加起来就是:6+4+2=12个了,那你就大错特错了,里面明显出现了重复的,我们现在要处理的就是如何去掉那些重复的了
第三步:这里就需要用到容斥原理了,公式就是:n/2+n/3+n/5-n/(2×3)-n/(2×5)-n/(3×5)+n/(2×3×5).
第四步:我们该如何实现呢?我在网上看到有几种实现方法:dfs(深搜),队列数组,位运算三种方法都可以 上述公式有一个特点:n除以奇数个数相乘的时候是加,
n除以偶数个数相乘的时候是减(奇加偶减)。这里用队列数组实现:我们可以把第一个元素设为-1 具体看代码实现
HDU 1796
Problem Description
Input
Output
Sample Input
Sample Output
题目要求
给出N和一个数集 求1—N中可以被数集中的任何元素整除的所有数的个数
参考AC代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| using namespace std; LL n,ans; int m,cnt; int a[15]; LL gcd(LL a,LL b) { return b==0?a:gcd(b,a%b); } void dfs(int cur,LL lcm,int id) { lcm=a[cur]/gcd(a[cur],lcm)*lcm; if(id&1) ans+=(n-1)/lcm; else ans-=(n-1)/lcm; for(int i=cur+1;i<cnt;i++) dfs(i,lcm,id+1); } int main() { /*freopen("input.txt","r",stdin);*/ while(scanf("%lld%d",&n,&m)!=EOF) { cnt=0,ans=0; memset(a,0,sizeof(a)); for(int i=0;i<m;i++) { int x; scanf("%d",&x); if(x!=0) //x为非负数 需要把0排除 a[cnt++]=x; } for(int i=0;i<cnt;i++) dfs(i,a[i],1); printf("%lld\n",ans); } return 0; }
|
思路
容斥原理地简单应用。先找出1…n内能被集合中任意一个元素整除的个数,再减去能被集合中任意两个整除的个数,即能被它们两只的最小公倍数整除的个数,
因为这部分被计算了两次,然后又加上三个时候的个数,然后又减去四个时候的倍数…所以深搜,最后判断下集合元素的个数为奇还是偶,奇加偶减。
HDU 2841
Problem Description
Input
Output
Sample Input
Sample Output
题目要求
从(1,1)开始的N×M的格子上有树,从(0,0)点可以看到多少棵树
参考AC代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| using namespace std; vector<int>e[maxn]; int que[maxn]; void init() { for(int i=0;i<maxn;i++) e[i].clear(); for(int i=2;i<maxn;i++) { int t=i; for(int j=2;j*j<maxn;j++) { if(t%j==0) { e[i].push_back(j); while(t%j==0) t/=j; } } if(t>1) e[i].push_back(t); } } LL rongchi(int n,int s) { int num=0; que[num++]=1; for(int i=0;i<e[s].size();i++) { int ep=e[s][i]; if(ep>n) break; int k=num; for(int j=0;j<k;j++) que[num++]=que[j]*(-1)*ep; } LL sum=0; for(int i=0;i<num;i++) sum+=n/que[i]; return sum; } int main() { /*freopen("input.txt","r",stdin);*/ init(); int t; scanf("%d",&t); while(t--) { int m,n; scanf("%d%d",&m,&n); LL ans=n; for(int i=2;i<=m;i++) ans+=rongchi(n,i); printf("%lld\n",ans); } return 0; }
|
思路
因为ans初始化为n 所以从第二行开始为奇减偶加
经画图推敲可以发现如果A1/B1=A2/B2那么就有一棵树看不到,所以就是找出Ai/Bi有多少种。
再可以发现A/B中,如果A,B有大于1的公约数,则A=A’×D B=B’×D,那么A/B=A’/B’,也就是存在另外一组数和这种相等,则问题转换成有多少对互质的数。
本题和HDU 4135唯一的区别就是枚举i,从1-M中找与i互质的数,其中1<=i<=N。
容注意先预处理i的所有素因子,然后容斥就可以了。