传送门
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; }