HDU 2841 Visible Trees(容斥原理+解释)

Visible Trees

 There are many trees forming a m * n grid, the grid starts from (1,1). Farmer Sherlock is standing at (0,0) point. He wonders how many trees he can see.

If two trees and Sherlock are in one line, Farmer Sherlock can only see the tree nearest to him. 

Input
The first line contains one integer t, represents the number of test cases. Then there are multiple test cases. For each test case there is one line containing two integers m and n(1 ≤ m, n ≤ 100000)
Output
For each test case output one line represents the number of trees Farmer Sherlock can see.
Sample Input

2
1 1
2 3

Sample Output

1
5
题意:

大概就是有个m*n个点的矩形从(1,1)到(m,n),问从(0,0)出发直线看过去最多能看到几个点。

分析:

如果 (0,0)>(x,y)(0,0)>(x,y) ( 0 , 0 ) − > ( x , y ) 和 ( 0 , 0 ) − > ( x ′ , y ′ ) 两个向量共线,即 (0,0),(x,y),(x,y) ( 0 , 0 ) , ( x , y ) , ( x ′ , y ′ ) 三点共线,那后面的那个点就看不到了。
如果三点共线,那么向量 x,y ( x ′ , y ′ ) 一定可以表示成 (kx,ky) ( k x , k y ) ,因此对于一个数对x,y如果他们存在公因数,那么一定可以化简成最简,即互质的形式,那么这个互质的数对构成的向量应该是和原向量共线的,因此我们只能看到最前面那个互质的数对构成的点

因此题目转变成求区间 [1,m],[1,n] [ 1 , m ] , [ 1 , n ] 之间互质的对数

1)选取一个区间(为了优化选取小区间)比如说选取 [1,n] [ 1 , n ] ,枚举n里面的数i,然后对于每个数i我们看它在 [1,m] [ 1 , m ] 区间内能找到多少互质的数

2)对于枚举的每个i,我们可以使用容斥原理。我们可以把i进行质因数分解,得到cnt个互不相同的素因子,我们设性质 Aj A j 代表可以被i的素因子 pj p j pj p j 的幂次整除
那么我们完全可以先求出 |A1A2A3Acnt| | A 1 ⋃ A 2 ⋃ A 3 ⋯ ⋃ A c n t | 然后用总的个数m减这个数即对 |A1A2A3Acnt¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯| | A 1 ⋃ A 2 ⋃ A 3 ⋯ ⋃ A c n t ¯ | ,就得到了 |A¯¯¯¯1A¯¯¯¯2A¯¯¯¯cnt| | A ¯ 1 ⋂ A ¯ 2 ⋯ ⋂ A ¯ c n t | 即为所求

|A1A2A3Aj|=|Aj||AjAk|++(1)cnt+1|A1A2A3Acnt| | A 1 ⋃ A 2 ⋃ A 3 ⋯ ⋃ A j | = ∑ | A j | − ∑ | A j ⋂ A k | + ⋯ + ( − 1 ) c n t + 1 | A 1 ⋂ A 2 ⋂ A 3 ⋯ ⋂ A c n t |

|A1A2A3Acnt¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|=|A¯¯¯¯1A¯¯¯¯2A¯¯¯¯cnt|=|S||A1A2A3Aj| | A 1 ⋃ A 2 ⋃ A 3 ⋯ ⋃ A c n t ¯ | = | A ¯ 1 ⋂ A ¯ 2 ⋯ ⋂ A ¯ c n t | = | S | − | A 1 ⋃ A 2 ⋃ A 3 ⋯ ⋃ A j |

code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
int prime[maxn],cnt;
void divide(int n){//分解质因子
    cnt = 0;
    for(int i = 2; i * i <= n; i++){
        if(n % i == 0){
            prime[cnt++] = i;
            while(n % i == 0) n /= i;
        }
    }
    if(n != 1) prime[cnt++] = n;
}
int solve(int S){
    int ans = 0;
    for(int i = 1; i < (1<<cnt); i++){//共有2^cnt中可能组合因为cnt不大可以枚举
        int tmp = 1,num = 0;
        for(int j = 0; j < cnt; j++){
            if(i & (1<<j)){
                tmp *= prime[j];//得到质因子的组合
                num++;
            }
        }
        if(num & 1) ans += S / tmp;//得到m内包含这几个质因子的数有多少,可以有倍数每个质因子个数可以不一定,类似求阶乘中因子
        else ans -= S / tmp;
    }
    return S - ans;
}
int main(){
    int t,n,m;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&m,&n);
        if(n > m) swap(m,n);
        ll ans = 0;
        for(int i = 1; i <= n; i++){
            divide(i);
            ans += solve(m);
        }
        printf("%lld\n",ans);
    }
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值