题意:有一块(1,1)到(m,n)的地,从(0,0)看能看到几块(如果两块地到看的地方三点一线,后面的地都看不到)。
思路:一开始是想不到容斥...后来发现被遮住的地都有一个特点,若(a,b)有gcd(a,b)!= 1,那么就会被遮住。因为斜率k一样,后面的点会被遮住,如果有gcd,那么除一下就会变成gcd = 1的那个点的斜率了。所以问题转化为求gcd不为1有几个点,固定一个点,然后容斥。
#include<set> #include<map> #include<queue> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ll long long using namespace std; const int maxn = 1000 + 10; const int seed = 131; const int MOD = 1000000000 + 7; const int INF = 0x3f3f3f3f; int prime[maxn], p[maxn], pn; int a[maxn], an; void get(){memset(p, 0, sizeof(p));pn = 0;for(ll i = 2; i < maxn; i++){if(!p[i]){prime[pn++] = i;for(ll j = i * i; j < maxn; j += i)p[j] = 1;}} } int main(){int n, m;int T;get();scanf("%d", &T);while(T--){scanf("%d%d", &n, &m);ll ans = m;for(int i = 2; i <= n; i++){ll cnt = 0;//质因数分解an = 0;int x = i;for(int j = 0; prime[j] * prime[j] <= x && j < pn; j++){if(x % prime[j] == 0){a[an++] = prime[j];while(x % prime[j] == 0){x /= prime[j];}}}if(x > 1) a[an++] = x;//求gcd不为1for(int j = 1; j < (1 << an); j++){int num = 0;ll val = 1;for(int k = 0;k < an; k++){if(j & (1 << k)){num++;val *= a[k];}}if(num & 1) cnt += m / val;else cnt -= m / val;}ans += m - cnt;}printf("%lld\n", ans);}return 0; }