• TOJ---3637---容斥原理


    Ah 这题 我是被深深地伤害了.....太莫名地TLE了  ==说....

    先上题 ~

          touch  me

    关于容斥定理 --- 这也是我的处女作

    其实这题 一开始 我是想用欧拉函数做的 -> 欧拉函数 一般是计算1~n内与n互质的数的个数  这里的1一般是要根据题目要求来考虑

    欧拉函数的 推导过程 真的好麻烦 理解起来好烦 与各种定理一起出现~~

    但这题 很特殊 它是问一个区间[l,r]对一个数n 是互质的关系的个数  ---  互质 就是最大公约数是 1

    so 我们要换种方法 使用一个叫容斥原理的东西..

    它的思想:from百度:

    先不考虑重叠的情况,把包含于某内容中的所有对象的数目先计算出来,然后再把计数时重复计算的数目排斥出去,使得计算的结果既无遗漏又无重复,这种计数的方法称为容斥原理。

    这里 我们首先为了方便计算 将[ l , r ] 对n的互质的个数通过 [ 1 ,r ] - [ 1 , l-1 ] 来求得

    求互质 我们可以用逆向思维来做  找出非互质的 即最大公约数 >1的数X 然后总数SUM来 减去 这个X 即Y=互质数

    每个合数 都可以用 质数的 乘积来表示  如 12 = 2 *2*3   10 = 2*5  8 = 2*2*2 …………

    你可以看到 12 10上的最大公约数是2 因为他们含有共同的 质因子2   这边 其实 我们并不需要刻意去求出 它们的最大公约数 只要证明2者之间有非1的质因子就可以

    那么 我们首先预处理n 求出它有哪些 质因子 存储到一个数组中去

    然后 我们假设 它有 2 3 5这3个质因子

    那么 你也看到了 容斥原理 是先将它的所有组合方案求出来  这边的数量当然是7 即 2 3 5 (2,3),(2,5),(3,5),(2,3,5)

    接下去 对于每个方案的处理是最关键的  用了很cool的二进制来实现   因为二进制 每位上是0或1  这里一旦有一位上出现1 我们就表示 这里有一个质因子出现 我这边为了更明了 我将1~7 各自的二进制 全给写出来 001 010 011 100 101 110 111 你可以看到 以每个二进制为考虑对象 一共出现3个(1个1) 3个(2个1) 1个(3个1) 完美对应上面方案的质因子数

    那你可能会想  --- 怎么去判断0 1位呢? 我们通过很神奇的 & 来实现---2位都是1 才是1

    这个 我会在 == 的代码里 我觉得这边 自己分析不好 就不写了~~

    然后 最后讲下 应该刚刚介绍 容斥原理时就应该讲的一个公式

    S(AuBuC) = S(A)+S(B)+S(C)-S(A∩B)-S(B∩C)-S(C∩A)+S(A∩B∩C)

    这里的A B C各自表示一个集合.. 嗯 我去画个图 就好理解了~

    这边 我分开区域 只是为了当你和我起初一样 第一次接触的时候 更好理解嘛~

    最后 讲一个被深深打击的地方了

    我在TOJ上Long long 用 lld  TLE 使用 I64d AC    =-=

    感谢 看完 又臭又长的叙述 ~   最后 贴 代码

     1 #include <iostream>
     2 using namespace std;
     3 
     4 #define LL long long
     5 const int size = 10100;
     6 int fact[size];
     7 LL slove (LL n, LL r)
     8 {
     9     int cnt = 0;
    10     for (int i=2; i*i<=n; ++i)
    11     {
    12         if (n % i == 0) 
    13         {
    14             fact[cnt++] = i;
    15             while (n % i == 0)
    16                    n /= i;
    17         }
    18     }// 这个 for 就是查找传入的参数 n 有哪些质因子 因为while的存在 所以 fact数组内存的数肯定都是质数且都是n的因子
    19     if (n > 1)
    20         fact[cnt++] = n; // 这里n 未除尽 添入数组
    21     LL sum = 0;
    22     for (int msk=1; msk < ( 1<<cnt ) ; ++msk) //这边 1<<cnt 是指如果这里有 2 3 5这3个质因子 那么可以构成2 3 5 (2,3),(2,5),(3,5),(2,3,5)这7种情况 所以循环是[1,8)
    23     {
    24         int temp = 1; // 这就是 存储下面的 fact[i]的并且实现累乘
    25         int var = 0;
    26         for (int i=0; i<cnt; ++i )
    27         {                        // 这边 相当于将二进制每个下标为1时 看成1个质因子的存在 如0010  0100是存在一个 0110 存在2个 1110存在3个..
    28             if ( msk & (1<<i) )  // 通过 & 操作 来确定是几个质因子 进行计算
    29             {                     // 如当 msk = 2 cnt = 3时 2&1 = 0   2&2 = 1  2&4 = 0可以看出只有一个因子进行了计算 2的二进制->010
    30                 ++var;         //  当 msk = 3  cnt = 3时 3&1 = 1  3&2 = 1 3&4 = 0可以看出有2个因子进行了计算 3的二进制->011 符合上面提到的
    31                 temp *= fact[i]; 
    32             }
    33         }
    34         LL cur = r / temp; // 计算是 此时 乘积 的倍数的个数为多少
    35         if ( var % 2 == 1 ) // 这里应该是根据 S(AuBuC) = S(A)+S(B)+S(C)-S(A∩B)-S(B∩C)-S(C∩A)+S(A∩B∩C)来判断 看括号里有几个数判断+-符号
    36             sum += cur;  
    37         else
    38             sum -= cur;
    39     }
    40     return r - sum;//因为sum求出的是整除这些质数的数 我们要求的是与n互质 所以减去
    41 }
    42 
    43 int main()
    44 {
    45     int t;
    46     LL l , r , n;
    47     while( ~scanf("%d",&t) )
    48     {
    49         for( int i = 1 ; i<=t ; i++ )
    50         {
    51             scanf( "%lld %lld %lld",&l,&r,&n );
    52             printf( "Case #%d: %lld
    ",i,slove(n,r)-slove(n,l-1) );// 分别计算[1,R]与[1,L-1]来相减.------l 1真的好难区分
    53         }
    54     }
    55     return 0;
    56 }
    View Code
    just follow your heart
  • 相关阅读:
    jQuery
    jQuery
    jQuery
    jQuery
    jQuery
    JavaScript DOM 编程艺术
    JavaScript DOM 编程艺术
    JavaScript DOM 编程艺术
    【免费】Linux命令行与Shell脚本编程大全 第3版 PDF全本 21MB 百度网盘下载
    可重入函数(转载)
  • 原文地址:https://www.cnblogs.com/radical/p/3794739.html
Copyright © 2020-2023  润新知