Visible Trees
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 2404 Accepted Submission(s): 1002
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
给定一个 m * n 的方格,左下角的点为(1,1),右上角的点(n,m),然后每个方格都是一棵树,然后一个人站在(0,0)点,往森林里看,当两棵树在一条线上的时候,它只能看到最前面的那个点,后面的树就不计了,计算能够看到多少颗树。
解题思路:
我们通过观察可以发现当(x,y)的GCD不是1的时候就不用计算,假设(x,y)和(x1,y1),必有 x/x1 == y/y1;
GCD(x,y)== x/x1 == y/y1 != 1;所以我们要求的就是在 1 - n中每个数 与 1 - m中互素的个数之和,但是呢,这样考虑如果用GCD来算的话,肯定会超时,所以我们才用它的对立面,也就是容斥原理来算。我们可以考虑1与[1,m]里面多少个数互质,2与[1,m]里面多少个数互质,3与[1,m]里面多少个数互质……n与[1,m]里面多少个数互质,把这些结果全部累加起来即可我们先算x在1 - m区间中互素的个数,那么我们要是用容斥原理的话,那么就是求在1 - m区间内与 x 不互素的个数,那么我们要求1 - m 中与 x 有一个素因子的个数,有两个素因子的个数……,然后满足奇家偶减,有奇数个素因子的数加,偶数个的减。最后得到的就是在 1 - m区间中与x不互素的个数,用n减掉,然后再1-n循环跑一遍就行,我用两个代码 一个是直接用 dfs 容斥的(这个不是很理解),还有一个就是 二进制枚举的(个人比较推荐这个 容易理解)
My Code:(二进制枚举)
/**
id:ITAK
Exe.Time:62ms
Exe.Memory:5880k
**/
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 1e5+5;
typedef long long LL;
/**
prime[i][j] 表示对 i 进行素因子分解,素因子存在 j 中
**/
int prime[MAXN][10];
int cnt[MAXN];///素因子的种类数
void Init()
{
memset(cnt, 0,sizeof(cnt));
for(int i=2; i<MAXN; i++)
{
if(cnt[i])
continue;
prime[i][0] = i;
cnt[i] = 1;
for(int j=2; j*i<MAXN; j++)
prime[j*i][cnt[j*i]++] = i;
}
}
LL RongChi(int m, int n)
{
LL ret = 0;
for(int i=1; i<(1<<cnt[m]); i++)
{
int sum = 0;
LL ans = 1;
for(int j=0; j<cnt[m]; j++)
{
if(i & (1<<j))
{
sum++;
ans *= prime[m][j];
}
}
if(sum & 1)
ret += n/ans;
else
ret -= n/ans;
}
return n-ret;
}
int main()
{
///cout<<2*3*5*7*11*13*17*19<<endl;
Init();
/**
for(int i=0; i<20; i++)
{
for(int j=0; j<5; j++)
cout<<prime[i][j]<<' ';
cout<<endl;
}**/
int T, n, m;
cin>>T;
while(T--)
{
cin>>m>>n;
if(m < n)
swap(m, n);
LL ret = m;///i==1时 互素个数肯定是m
for(int i=2; i<=n; i++)
ret += RongChi(i,m);
printf("%I64d\n",ret);
}
return 0;
}
My Code:(dfs)
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 1e5+5;
typedef long long LL;
/**
prime[i][j] 表示对 i 进行素因子分解,素因子存在 j 中
**/
int prime[MAXN][10];
int cnt[MAXN];///素因子的种类数
void Init()
{
memset(cnt, 0,sizeof(cnt));
for(int i=2; i<MAXN; i++)
{
if(cnt[i])
continue;
prime[i][0] = i;
cnt[i] = 1;
for(int j=2; j*i<MAXN; j++)
prime[j*i][cnt[j*i]++] = i;
}
}
LL RongChi(int m, int n, int i)
{
LL ret = 0;
for(int j=i; j<cnt[m]; j++)
ret += n/prime[m][j]-RongChi(m,n/prime[m][j],j+1);
return ret;
}
int main()
{
///cout<<2*3*5*7*11*13*17*19<<endl;
Init();
/**
for(int i=0; i<20; i++)
{
for(int j=0; j<5; j++)
cout<<prime[i][j]<<' ';
cout<<endl;
}**/
int T, n, m;
cin>>T;
while(T--)
{
cin>>m>>n;
if(m < n)
swap(m, n);
LL ret = m;
for(int i=2; i<=n; i++)
ret+= m-RongChi(i,m,0);
printf("%I64d\n",ret);
}
return 0;
}