zoukankan      html  css  js  c++  java
  • 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 <cstdio>
    #include <cstring>
    #include <cmath>
    #include <vector>
    #include <queue>
    #include <algorithm>
    using namespace std;
    #define MAX 100010
    
    bool p[MAX];
    vector<int>fac[MAX];//保存每个数字带有的质因子
    
    void init()
    {
      for(int i=0; i<MAX; i++) fac[i].clear();
      memset(p,false,sizeof(p));
      for(int i=2; i<MAX; i++)
        if(!p[i]) //i是质因子
        {
          fac[i].push_back(i);
          for(int j=i+i; j<MAX; j+=i)
          {
            p[j]=true;  //不是质因子
            fac[j].push_back(i);  //j这个数字都含有质因子i
          }
        }
    }
    
    int cal(int n , int m)
    {
      int size=(int)fac[n].size(); //得到n这个数字有多少个质因子
      int maxs=1<<size;  //从中选一些质因子,就是生成子集,子集最多个数
      int Count=0; //记录n与1到m这m个数中多少个数互质
      for(int s=1; s<maxs; s++) //枚举子集,不能有空集所以从1开始
      {
        int k=0 , num=0 , pro=1;
        for(int i=0; i<size; i++)
          if(s&(1<<i)) //有第i个质因子
          {
            num++; //计数
            pro *= fac[n][i];  //乘上这个质因子
          }
        if( num&1 ) //选出了奇数个质因子,按照容斥原理加上
          Count += m/pro;
        else        //选出了偶数个质因子,按照容斥原理减去
          Count -= m/pro;
      }
      return  m-Count;
    }
    
    int main()
    {
      int T,H,W;
      init();//找出每个数字带有的质因子
      scanf("%d",&T);
      while(T--)
      {
        scanf("%d%d",&H,&W);
        if(W>H) //交换可令循环次数减少
        { H=H^W; W=H^W; H=H^W;}
        long long ans=H;
        for(int w=2; w<=W; w++)
          ans += cal(w,H);
        printf("%I64d\n",ans); //注意输出格式不能用%lld
      }
      return 0;
    }
  • 相关阅读:
    wxPython
    IT从业者职业规划
    成功开发iPhone软件的10个步骤
    开源认识:Jumony
    一个GG/MM的彩色验证码图片(C#)
    IT从业者学习规划
    为某一个对象动态添加属性
    .net 动态加载css与js
    文本框等css
    博客园配合得很好的代码插件
  • 原文地址:https://www.cnblogs.com/scau20110726/p/2966089.html
Copyright © 2011-2022 走看看