HDU 2841 Visible Trees (容斥原理+素因子分解):http://acm.hdu.edu.cn/showproblem.php?pid=2841
题面:
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2380 Accepted Submission(s): 989
2 1 1 2 3
1 5
题目大意:
初始值为站在(0,0)点,现有一个m*n(m行n列)的网格,每个格子中都会种树,计算从(0,0)能看着见数的棵树(如果有两棵树在同一条直线上,我们只能看到一棵树)。
题目分析:
根据题意,通过画图,对于任意一个点(x,y),x和y有非1的最大公约数d,则该点可以缩小为(x/d,y/d),将满足这样的点和(0,0)连起来,发现他们刚好在一条直线上,所以,我们只能看到这样的一棵树。所以,对于任意一点(x,y),我们可以先去判断x和y的最大公约数是否为1,即两个数是否互质,如果互质,则能看到这个点上所能种的树,否则,我们就只能看到(x/d,y/d)点上的树。
所以,我们就是要找到所有满足x属于[1,m]和y属于[1,n]且x和y互质的点。
具体实现:(转)
我们可以固定一个数字,用一个数来循环。例如矩阵为n*m,我们固定m,用n来循环,即1与[1,m]里面多少个数互质,2与[1,m]里面多少个数互质,3与[1,m]里面多少个数互质……n与[1,m]里面多少个数互质,把这些结果全部累加起来即可
所以问题的最后变为了,给定一个数字x,怎么找出它和1到y里面有多少个数互质呢?
两个数字互质,其实就是它们没有公共的质因子,反过来两个数字有公共的质因子则一定不互质,那么我们可以求反面,x与1到y里面多少个数字不互质,然后用y减去即可。
在这里我们就用到了容斥原理:先找到有多少个数和x有1个公共的质因子,然后加上;再找到有多少个数与x有2个公共的质因子,然后减去;再找到有多少个数有多少个数与x有3个公共的质因子,然后加上……最后得到的个数,就是有多少个数与x不互质
因为容斥原理一个最基本的准则就是——
要计算几个集合并集的大小,我们要先将所有单个集合的大小计算出来,然后减去所有两个集合相交的部分,再加回所有三个集合相交的部分,再减去所有四个集合相交的部分,依此类推,一直计算到所有集合相交的部分。(奇数加,偶数减)
代码实现:(递归的部分就是最核心的部分)
#include <iostream> #include <cstdio> using namespace std; int prime[100010][10]; int cnt[100010]= {0}; void Init() //筛法筛出所有的素因子,i的素因子分别存放在prime[i][j]里面 { //2*3*5*7*11*13*17>100000,所以二维数组的列数不用开太大 for(int i=2; i<=100000; i++) { if(cnt[i]!=0) continue; prime[i][0]=i; cnt[i]=1; for(int j=2; j*i<=100000; j++) { prime[i*j][cnt[i*j]++]=i; } } } long long dfs(int n,int m,int k) { long long ret=0; for(int i=k; i<cnt[n]; i++) { ret=ret+(m/prime[n][i]-dfs(n,m/prime[n][i],i+1)); } return ret; } int main() { int t; int m,n; Init(); scanf("%d",&t); while(t--) { scanf("%d%d",&m,&n); long long ans=m;//当i为1的时候,互素的数肯定为m个 for(int i=2; i<=n; i++) { ans=ans+(m-dfs(i,m,0)); //cout<<"ans= "<<ans<<" "; } printf("%I64d\n",ans); } return 0; }