HDU 2841 Visible Trees 数论+容斥原理

Posted Ritchie丶

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU 2841 Visible Trees 数论+容斥原理相关的知识,希望对你有一定的参考价值。

容斥原理

题意:给n*m的矩阵有点,左下角的点为(1,1),右上角的点(n,m),(其实转回来也是没影响的即m*n),一个人站在(0,0)看这些点,在一条直线的视线上,它只能看到最前面的那个点,后面的点将会被档住他看不到,问你,这个人一共能看到多少个点。

这个问题只要画一下图不难发现,如果一个点(x,y),x和y有非1的公约数z,那么他们其实可以一起缩小为(x/z,y/z),试着把这两个点和(0,0)连线,发现他们其实是同一条直线,而(x/z,y/z)

在前面,所以其实(x,y)被挡住了看不到的,这启发了我们,如果我们找到了x和y的最大公约数g,那么(x/g,y/g)一定是这条直线上最前面的点,没有其他店能挡住他,他一定能被看到,而他后面的点都看不到,那么(x/g,y/g)满足的性质就是,这两个数字互质

从而得到一个结论,两个数字(x,y)如果两数互质,则可以被看到,如果不互质,则看不到,所以我们就是要找出所有的二元组(x,y)使他们互质

我们可以固定一个数字,用一个数来循环。例如矩阵为n*m,我们固定m,用n来循环,即1与[1,m]里面多少个数互质,2与[1,m]里面多少个数互质,3与[1,m]里面多少个数互质……n与[1,m]里面多少个数互质,把这些结果全部累加起来即可

所以问题的最后变为了,给定一个数字x,怎么找出它和1到y里面有多少个数互质呢?

两个数字互质,其实就是它们没有公共的质因子,反过来两个数字有公共的质因子则一定不互质,那么我们可以求反面,x与1到y里面多少个数字不互质,然后用y减去即可

在这里我们就用到了容斥原理:先找到有多少个数和x有1个公共的质因子,然后加上;再找到有多少个数与x有2个公共的质因子,然后减去;再找到有多少个数有多少个数与x有3个公共的质因子,然后加上……最后得到的个数,就是有多少个数与x不互质

因为容斥原理一个最基本的准则就是——

要计算几个集合并集的大小,我们要先将所有单个集合的大小计算出来,然后减去所有两个集合相交的部分,再加回所有三个集合相交的部分,再减去所有四个集合相交的部分,依此类推,一直计算到所有集合相交的部分。(奇数加,偶数减)

#include <iostream>
#include <stdio.h>
using namespace std;
typedef long long ll;
bool isp[1010];
int prime[1010],cnt,pp[1010],cnt0,n,m;
ll ans;
void pr()
{
    cnt=0;
    for(int i=2;i<=1000;i++)
    {
        if(!isp[i])
        {
            prime[cnt++]=i;
            for(int j=i+i;j<=1000;j=j+i)
            isp[j]=true;
        }
    }
}
void cal(int cur)
{
    cnt0=0;
    for(int i=0;prime[i]*prime[i]<=cur;i++)
    {
        if(cur%prime[i]==0)
        {
            pp[cnt0++]=prime[i];
            while(cur%prime[i]==0)
            cur/=prime[i];
        }
    }
    if(cur!=1)
    pp[cnt0++]=cur;
}
void dfs(int hav,int cur,int num)
{
    if(hav>m||cur>=cnt0)
    return ;
    for(int i=cur;i<cnt0;i++)
    {
        int temp=hav*pp[i];
        if(num&1)
        ans-=m/temp;
        else
        ans+=m/temp;
        dfs(temp,i+1,num+1);
    }
}
int main()
{
    pr();
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        ans=m;
        for(int i=2;i<=n;i++)
        {
            cal(i);
            ans+=m;
            for(int j=0;j<cnt0;j++)
            {
                ans-=m/pp[j];
                dfs(pp[j],j+1,2);
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

 

以上是关于HDU 2841 Visible Trees 数论+容斥原理的主要内容,如果未能解决你的问题,请参考以下文章

hdu 2841 Visible Trees 容斥原理

Visible Trees HDU - 2841

Visible Trees HDU - 2841(容斥)

Visible Trees HDU - 2841(容斥)

C - Visible Trees HDU - 2841 -莫比乌斯函数-容斥

HDU2841 Visible Trees(容斥原理)