• 【HDU 4135 && HDU 2841 && HDU1695】 容斥定理+数论 (难度递增三步曲)


    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4135

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2841

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1695

    hdu 4135

    题目大意: 输入一个a,b,n。 让你求a~b中有多少个数和n互素。1和任何数都互素。

    解题思路:

        看到题我们不可能对i属于a~b进行遍历,然后求i是否和n有公约数,有则不互素,无则互素。时间复杂度是n^2  n最大100000,TLE。

        这题要用到容斥定理。

        我们可以先这样想:[1,n]中有多少个数和m互素,可以转换成[1,n]中有多少个数和m不互素(假设这个值为ans),那么互素当然就为n-ans。

        问题就变成了求[1,n]中有多少个数和m不互素。

        假设n=12,m=30

        第一步:首先求出m的因子数(m的因子数为2,3,5)。

        第二步:[1,n]中 是m因子的倍数当然就不互素了。

                 (2,4,6,8,10,12)->n/2   6个,  (3,6,9,12)->n/3  4个,(5,10)-> n/5 2个 。

                 心急的就可能全部相加了。莫急,有没有发现里面出现了重复的,所以我们还要减去重复的。

                公式就是  n/2+n/3+n/5-n/(2*3)-n/(2*5)-n/(3*5)+n/(2*3*5).

        第三步: 关键的来了。这一步有多种方法,dfs,队列数组,位运算都行,队列数组比dfs快一点。这里我只讲第二种(队列数组), 我们可以用一个队列(数组也行)存储出现的分母,我们可以令队列的第一个元素为1,让每次出现的m的因子和队列中的元素一个一个相乘再存储到队列中,最后就会发现存储的元素就是我们上面的分母了。现在的问题又变成了我们时候用加什么时候用减,这里我们只需要每次存的时候在再乘一个(-1),就可以得到我们想要的结果了。

     题目说要求[a,b]中与n互素的,我们分别求出[1,b]与n互素的以及[1,a-1]与n互素的,两个相减就是答案了。

    代码:

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <algorithm>
     4 #include <cstring>
     5 #include <vector>
     6 using namespace std;
     7 vector<int>vt;
     8 
     9 __int64  n, a, b, res;
    10 __int64 que[1024];
    11 
    12 void fx()
    13 {
    14     vt.clear();
    15     res=n;
    16     for(int i=2; i*i<=n; i++)
    17     {
    18         if(res%i==0)
    19         {
    20           vt.push_back(i);
    21           while(res%i==0)
    22                res/=i;
    23         }
    24     }
    25     if(res>1)  vt.push_back(res);
    26 }
    27 
    28 __int64 cal(__int64 n, __int64 t)
    29 {
    30     int num=0;
    31     que[num++]=1;
    32     for(int i=0; i<vt.size(); i++)
    33     {
    34         int ep=vt[i];
    35         int k=num;
    36         for(int j=0; j<k; j++)
    37             que[num++]=ep*que[j]*(-1);
    38     }
    39     __int64 sum=0;
    40     for(int i=0; i<num; i++)
    41         sum+=t/que[i];
    42     return sum;
    43 }
    44 
    45 int main()
    46 {
    47     int  T, tcase=0;
    48     cin >> T;
    49     while(T--)
    50     {
    51         cin >> a >> b >> n;
    52         fx();
    53         __int64 ans=cal(n,b)-cal(n,a-1);
    54         printf("Case #%d: %I64d\n",++tcase,ans);
    55     }
    56     return 0;
    57 }

     hdu 2841

    题目大意:   N*M的格点上有树,从0,0点可以看到多少棵树。

    解题思路:

    经画图推敲可以发现如果A1/B1=A2/B2那么就有一棵树看不到,所以就是找出Ai/Bi有多少种。

    再可以发现A/B中,如果A,B有大于1的公约数,则A=A'*D B=B'*D,那么A/B=A'/B',也就是存在另外一组数和这种相等,则问题转换成有多少对互质的数。

    本题和上一题唯一的区别就是枚举i,从1-M中找与i互质的数,其中1<=i<=N。

    容注意先预处理i的所有素因子,然后容斥求就可以了。

    代码:

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <algorithm>
     4 #include <cstring>
     5 #include <vector>
     6 using namespace std;
     7 
     8 const int maxn=100001;
     9 int que[maxn];
    10 vector<int>vt[maxn];
    11 
    12 void init()
    13 {
    14     for(int i=0; i<maxn; i++)
    15         vt[i].clear();
    16     for(int i=2; i<maxn; i++)
    17     {
    18         int t=i;
    19         for(int j=2; j*j<=i; j++)
    20         {
    21             if(t%j==0)
    22             {
    23                 vt[i].push_back(j);
    24                 while(t%j==0)
    25                    t/=j;
    26             }
    27         }
    28         if(t>1) vt[i].push_back(t);
    29     }
    30 }
    31 
    32 __int64 cal(int n, int s)
    33 {
    34     int num=0;
    35     que[num++]=1;
    36     for(int i=0; i<vt[s].size(); i++)
    37     {
    38         int ep=vt[s][i];
    39         if(ep>n) break;
    40         int k=num;
    41         for(int j=0; j<k; j++)
    42         {
    43             que[num++]=que[j]*ep*(-1);
    44         }
    45     }
    46     __int64 sum=0;
    47     for(int i=0; i<num; i++)
    48     {
    49         sum+=n/que[i];
    50     }
    51     return sum;
    52 }
    53 
    54 int main()
    55 {
    56     int T, n, m;
    57     init();
    58     cin >> T;
    59     while(T--)
    60     {
    61         scanf("%d%d",&n,&m);
    62         __int64 ans=n;
    63         for(int i=2; i<=m; i++)
    64             ans+=cal(n,i);
    65         printf("%I64d\n",ans);
    66     }
    67     return 0;
    68 }

    hdu1695

    题目大意:给你5个数a,b,c,d,k。x属于[a,b]y属于[c,d]。 问你有多少对(x,y)的公约数为k。  注意(x,y)和 (y,x)视为同一对。

    解题思路:

     本题用到了容斥定理+数论素数筛选法+数论欧拉函数。 不失为一个好题。

     注意看清楚题目开头解释, 你可以假想a=c=1,有了这个就更简单了。 我们可以先令端点b,d分别除以k,b/=k,d/=k。

      b可能大于d,为了方便求解这里我们令d大于b,如果不是则互换。这样就只需要找[1,b],[1,d]中有多少对互素的数了。

    我们令i从1~d进行遍历:

    1、当i<=b时,可以直接用欧拉函数求出互素的对数。

    2、当i>b时,利用容斥定理求[1,b]中与i互素的对数。

    这里注意特判一下k=0的情况,藐视就是没注意这里running error time 几次。

    本题用容斥定理我用了两种方法,队列数组和dfs,练练手感。队列数组比dfs快一倍。

    代码:

      1 #include <iostream>
      2 #include <cstdio>
      3 #include <algorithm>
      4 #include <cstring>
      5 #include <vector>
      6 using namespace std;
      7 
      8 const int maxn=100005;
      9 int que[maxn];
     10 bool color[maxn];
     11 int f[maxn], phi[maxn];
     12 vector<int>vt[maxn];
     13 
     14 void Eular()  //欧拉函数
     15 {
     16     phi[1]=1;
     17     int k, num=0;
     18     memset(color,false,sizeof(color));
     19     for(int i=2; i<maxn; i++)
     20     {
     21         if(!color[i])
     22         {
     23             f[num++]=i;
     24             phi[i]=i-1;
     25         }
     26         for(int j=0; j<num&&(k=i*f[j])<maxn; j++)
     27         {
     28             color[k]=true;
     29             if(i%f[j]==0)
     30             {
     31                 phi[k]=phi[i]*f[j]; break;
     32             }
     33             else
     34                 phi[k]=phi[i]*(f[j]-1);
     35         }
     36     }
     37 }
     38 
     39 void init()   //打表存因子
     40 {
     41     for(int i=2; i<maxn; i++)
     42     {
     43         int t=i;
     44         for(int j=0; f[j]*f[j]<=i; j++)
     45         {
     46             if(t%f[j]==0)
     47             {
     48                 vt[i].push_back(f[j]);
     49                 while(t%f[j]==0)
     50                    t/=f[j];
     51             }
     52         }
     53         if(t>1) vt[i].push_back(t);
     54     }
     55 }
     56 
     57 __int64 cal(int n, int s) //队列数组实现容斥定理
     58 {
     59     int num=0;
     60     que[num++]=1;
     61     for(int i=0; i<vt[s].size(); i++)
     62     {
     63         int ep=vt[s][i];
     64         if(ep>n) break;
     65         int k=num;
     66         for(int j=0; j<k; j++)
     67         {
     68             que[num++]=que[j]*ep*(-1);
     69         }
     70     }
     71     __int64 sum=0;
     72     for(int i=0; i<num; i++)
     73     {
     74         sum+=n/que[i];
     75     }
     76     return sum;
     77 }
     78 
     79 /*
     80 __int64 dfs(int a, int b, int cur)  //dfs实现容斥定理
     81 {
     82     __int64 res=0, k;
     83     for(int i=a; i<vt[cur].size(); i++)
     84     {
     85         k=b/vt[cur][i];
     86         res+=k-dfs(i+1,k,cur);
     87     }
     88     return res;
     89 }
     90 */
     91 
     92 int main()
     93 {
     94     int  a, b, c, d, k, T, tcase=0;
     95     Eular();
     96     init();
     97     cin >> T;
     98     while(T--)
     99     {
    100         scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
    101         if(k==0||k>b||k>d)
    102         {
    103             printf("Case %d: 0\n",++tcase); continue;
    104         }
    105         b=b/k, d=d/k;
    106         if(b>d) swap(b,d);
    107         __int64 ans=0;
    108         for(int i=1; i<=b; i++)
    109         {
    110             ans+=phi[i];
    111         }
    112         for(int i=b+1; i<=d; i++)
    113         {
    114             ans+=cal(b,i);
    115         }
    116         printf("Case %d: %I64d\n",++tcase,ans);
    117     }
    118     return 0;
    119 }

     

  • 相关阅读:
    用C语言编写生成小学四则运算程序
    每周学习报告
    读现代软件工程有感和自我介绍
    第七天
    第五天
    第六天
    作业九:课程总结
    作业四:结对编程项目--四则运算
    psp记录个人项目花费时间
    作业三:代码规范,代码复查
  • 原文地址:https://www.cnblogs.com/kane0526/p/2795446.html
Copyright © 2020-2023  润新知