题意:有一块(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不为1 for(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 ", ans); } return 0; }