Visible Trees (HDU-2841)(容斥原理)

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),问可以看到几棵树。

思路:这道题的话,因为当两棵树在一条直线上的时候我们看不到,所以我们判断一下一个点(x,y)能被看到的条件。就是当x,y互质的时候,这颗树才能被看见。所以问题就转换成了有多少对坐标是互质的。可以通过枚举x,看有几个y与其互质累加。这样问题就又变成,区间有几个数与某个数互质,就转变成了经典的容斥问题。

AC代码:

#include <bits/stdc++.h>
typedef long long ll;
const int maxx=100010;
const int inf=0x3f3f3f3f;
using namespace std;
int p[maxx];
int k;
void getp(ll n)
{
    k=0;
    for(ll i=2; i*i<=n; i++)
    {
        if(n%i==0)
        {
            p[k++]=i;
            while(n%i==0)
                n/=i;
        }
    }
    if(n>1)
        p[k++]=n;
}
ll nop(ll m)
{
    ll top=0;
    ll que[maxx];
    ll t;
    que[top++]=-1;
    for(int i=0; i<k; i++)
    {
        t=top;
        for(int j=0; j<t; j++)
        {
            que[top++]=que[j]*p[i]*(-1);
        }
    }
    ll sum=0;
    for(ll i=1; i<top; i++)
        sum+=(m/que[i]);
    return sum;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        ll m,n;
        scanf("%lld%lld",&m,&n);
        if(m>n)
            swap(m,n);
        ll ans=0;
        for(ll i=1; i<=m; i++)
        {
            getp(i);
            ans+=(n-nop(n));
        }
        printf("%lld\n",ans);
    }
    return 0;
}


下面再给出容斥原理的二进制版(位运算)模板:

#include <bits/stdc++.h>
 
using namespace std;
 
int gcd(int a,int b)
{
    return b==0?a:gcd(b,a%b);
}
int lcm(int a,int b)
{
    return a*b/gcd(a,b);
}
int solve()//求1~n与n互质的数的个数
{
    int prime[10]= {2,3,5};
    int n=30,ans,num;
    ans=0,num=3;//num是素数因子或者因子的个数,这里是素数因子
    for(int i=1; i< (1<<num) ; i++)//循环次数,也就是集合个数,也就是有2^num-1个集合
    {
        //i就是表示第i个集合
        //2^n可以用1<<n为运算表示
        int mult=1,cnt=0;
        for(int j=0; j<num; j++) //循环prime中的每个元素
        {
            if(i & (1<<j))//要掌握&运算,与i的二进制的第j位比较,看是否为1,是则选中
            {
                cnt++;//记录集合中元素的个数
                mult = lcm(mult,prime[j]);//是否要lcm看情况
            }
        }
        if(cnt&1)//集合元素个数为奇数就加,否则减
            ans += n/mult;
        else
            ans -= n/mult;
    }
    return n-ans;
}
int main()
{
    cout<<solve();
    return 0;
}

 

用位运算模板AC本题的代码:

#include <bits/stdc++.h>
 
using namespace std;
typedef long long ll;
int fac[100000];
int div(int n)
{
    int cnt=0;
    for(int i=2;i*i<=n;i++)
    {
        if(n%i==0)
        {
            fac[cnt++]=i;
            while(n%i==0) n/=i;
        }
    }
    if(n>1) fac[cnt++]=n;
    return cnt;
}
int solve(int n,int cnt)
{
    int ans=0;
    for(int i=1;i< (1<<cnt);i++)
    {
        int ones=0,mult=1;
        for(int j=0;j<cnt;j++)
        {
            if(i & (1<<j))
            {
                ones++;
                mult*=fac[j];
            }
        }
        if(ones &1)
            ans+= n/mult;
        else
            ans-= n/mult;
    }
    return n-ans;
}
int main()
{
   int t;
   cin>>t;
   while(t--)
   {
       int n,m;
       ll ans=0;
       cin>>n>>m;
       for(int i=1;i<=n;i++)
       {
           int cnt=div(i);
           ans+= solve(m,cnt);
       }
       cout<<ans<<endl;
   }
    return 0;
}

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值