HDU 2841 Visible Trees (容斥原理好题)

Visible Trees 
Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u

Description

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),右上角的点(n,m) ,然后每个方格都是一棵树,然后一个
人站在(0,0)点,往森林里看,当两棵树在一条线上的时候 ,它只能看到最前面的那个点, 后面的树就不计
了,计算能够看到多少颗树。
尴尬的是我还理解错题意好几遍 【汗】
对于(x,y)之间如果有公约数为D(D!=1),x1/y1=x/D/(y/D)所以(x,y)就被挡住了,如果(x,y)
之间有公约数,那么(X,y)就被挡住了,N*M-被挡住的(x,y)就是答案。1-m和1-n的公约数用Gcd判断肯
定会超时,所以就考虑1到【1-m】之间有多少个数不互质,2到【1-m】之间有多少个数不互质……n到【1-m】
之间有多少个数不互质。n*M减去这些数就是答案。怎么转化成容斥原理的代码呢,把他们转化为有几个素因子
(素因子!!我看了长时间代码才注意到这个素字。)奇数加偶数减正是容斥原理的核心,举个小栗子12到【1-16】之间有多少个数不互质呢?12的素因子有2,3没了,4不是素的是荤的-_-. 16/2-16/2/3+16/3就是12行16个数中
有几个被挡住的16/2是代表有2 4 6 8……16共8个和12有公约数,16/3是3,6……15共16/3即5个和12有公约数3
必须减去6.,12这些算两次的,所以明白了原理,下面上代码,希望有大家有帮助,不懂请留言:


挡住我的时间长,收获也大,
enjoy coding, enjoy sharing! 
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 1e5+5;
typedef __int64 LL;




int prime[MAXN][20];  ///每个数有几个素因子 一般不超过20 打表
int cnt[MAXN];///i有几个素因子
void Init()
{
    memset(cnt, 0,sizeof(cnt));
   for(int i=2;i<MAXN;i++)
   {
       if(cnt[i])continue;///有了素因子就不能当做素因子了
       cnt[i]=1;
       prime[i][0]=i;///自身算一个素因子
       for(int j=2;i*j<MAXN;j++)
       {
           prime[j*i][cnt[i*j]++]=i;///cnt[i*j]的个数加一
       }
   }
}
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()
{


    Init();


    int T, n, m;
    cin>>T;
    while(T--)
    {
        cin>>m>>n;


        LL ret = m;


      for(int i=2;i<=n;i++)
        ret+=m-RongChi(i,m,0);
        printf("%I64d\n",ret);
    }
    return 0;
}

功未成,身未退。
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值