HDU 2841 Visible Trees(容斥)题解

题意:有一块(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\n", ans);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/KirinSB/p/9568783.html