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
If two trees and Sherlock are in one line, Farmer Sherlock can only see the tree nearest to him.
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;
}