HDU 2841 Visible Trees(容斥原理)

传送门

Visible Trees

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2404    Accepted Submission(s): 1002


Problem 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
 

Source
2009 Multi-University Training Contest 3 - Host by WHU
 
题目大意:

给定一个 m * n 的方格,左下角的点为(1,1),右上角的点(n,m),然后每个方格都是一棵树,然后一个人站在(0,0)点,往森林里看,当两棵树在一条线上的时候,它只能看到最前面的那个点,后面的树就不计了,计算能够看到多少颗树。


解题思路:

我们通过观察可以发现当(x,y)的GCD不是1的时候就不用计算,假设(x,y)和(x1,y1),必有 x/x1 ==  y/y1;

GCD(x,y)== x/x1 == y/y1 != 1;所以我们要求的就是在 1 - n中每个数 与 1 - m中互素的个数之和,但是呢,这样考虑如果用GCD来算的话,肯定会超时,所以我们才用它的对立面,也就是容斥原理来算。我们可以考虑1与[1,m]里面多少个数互质,2与[1,m]里面多少个数互质,3与[1,m]里面多少个数互质……n与[1,m]里面多少个数互质,把这些结果全部累加起来即可我们先算x在1 - m区间中互素的个数,那么我们要是用容斥原理的话,那么就是求在1 - m区间内与 x 不互素的个数,那么我们要求1 - m 中与 x 有一个素因子的个数,有两个素因子的个数……,然后满足奇家偶减,有奇数个素因子的数加,偶数个的减。最后得到的就是在 1 - m区间中与x不互素的个数,用n减掉,然后再1-n循环跑一遍就行,我用两个代码 一个是直接用 dfs 容斥的(这个不是很理解),还有一个就是 二进制枚举的(个人比较推荐这个 容易理解)

My Code:(二进制枚举)

/**
id:ITAK
Exe.Time:62ms
Exe.Memory:5880k
**/
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 1e5+5;
typedef long long LL;
/**
prime[i][j] 表示对 i 进行素因子分解,素因子存在 j 中
**/
int prime[MAXN][10];
int cnt[MAXN];///素因子的种类数
void Init()
{
    memset(cnt, 0,sizeof(cnt));
    for(int i=2; i<MAXN; i++)
    {
        if(cnt[i])
            continue;
        prime[i][0] = i;
        cnt[i] = 1;
        for(int j=2; j*i<MAXN; j++)
            prime[j*i][cnt[j*i]++] = i;
    }
}
LL RongChi(int m, int n)
{
    LL ret = 0;
    for(int i=1; i<(1<<cnt[m]); i++)
    {
        int sum = 0;
        LL ans = 1;
        for(int j=0; j<cnt[m]; j++)
        {
            if(i & (1<<j))
            {
                sum++;
                ans *= prime[m][j];
            }
        }
        if(sum & 1)
            ret += n/ans;
        else
            ret -= n/ans;
    }
    return n-ret;
}
int main()
{
    ///cout<<2*3*5*7*11*13*17*19<<endl;
    Init();
    /**
    for(int i=0; i<20; i++)
    {
        for(int j=0; j<5; j++)
            cout<<prime[i][j]<<' ';
        cout<<endl;
    }**/
    int T, n, m;
    cin>>T;
    while(T--)
    {
        cin>>m>>n;
        if(m < n)
            swap(m, n);
        LL ret = m;///i==1时 互素个数肯定是m
        for(int i=2; i<=n; i++)
            ret += RongChi(i,m);
        printf("%I64d\n",ret);
    }
    return 0;
}


My Code:(dfs)

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 1e5+5;
typedef long long LL;
/**
prime[i][j] 表示对 i 进行素因子分解,素因子存在 j 中
**/
int prime[MAXN][10];
int cnt[MAXN];///素因子的种类数
void Init()
{
    memset(cnt, 0,sizeof(cnt));
    for(int i=2; i<MAXN; i++)
    {
        if(cnt[i])
            continue;
        prime[i][0] = i;
        cnt[i] = 1;
        for(int j=2; j*i<MAXN; j++)
            prime[j*i][cnt[j*i]++] = i;
    }
}
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()
{
    ///cout<<2*3*5*7*11*13*17*19<<endl;
    Init();
    /**
    for(int i=0; i<20; i++)
    {
        for(int j=0; j<5; j++)
            cout<<prime[i][j]<<' ';
        cout<<endl;
    }**/
    int T, n, m;
    cin>>T;
    while(T--)
    {
        cin>>m>>n;
        if(m < n)
            swap(m, n);
        LL ret = m;
        for(int i=2; i<=n; i++)
            ret+= m-RongChi(i,m,0);
        printf("%I64d\n",ret);
    }
    return 0;
}



你可能感兴趣的:(容斥原理)