zoukankan      html  css  js  c++  java
  • The 19th Zhejiang University Programming Contest Sponsored by TuSimple (Mirror) B"Even Number Theory"(找规律???)

    传送门

    题意:

      给出了三个新定义:

    1. E-prime : ∀ num ∈ E,不存在两个偶数a,b,使得 num=a*b;(简言之,num的一对因子不能全为偶数)
    2. E-prime factorization : 定义集合P由 E-prime 元素组成,定义 e = p1*p2*.....*pn;(p1,p2,....,pn ∈ P , |P| = n)
    3. E-factorial : 定义 e!! = 2*4*6*8*.........*e;(简言之,偶数e及其之前的偶数连乘积)

      输出一个数 e ,求满足条件2的集合P,并且集合P需满足:p1*p2*.....*p= e!! , |P| = n;

      求 n 的最大值;

     题解:

      定义集合 Pi 为偶数 i 的满足条件(2)的最大的集合

      例如P8 = {2,2,2},| P8 | = 3;

      根据贪心的思想,要想使集合 Pe!! 中的元素个数最多,那么,e!! 一定要优先分解出最多的2(最小的e-prime);

      定义 odd 表示奇数,那么 odd*2 为 e-prime,因为 odd 因式分解肯定没有偶数因子,odd*2 只能分解出一个偶数因子2,符合条件(1);

      因此,对于所有的奇数 odd,可以通过让 odd*2 将 odd 转化为 e-prime,那么,我们的关注点就变成了 e!! 最多能分解出多少个2

      对于 e 之前的所有偶数,假设 ≤ e 的最大的2的幂为 2x+1,那么对于 e 之前的所有2的幂 21,22,23,....,2x+1 其可分解出 1+2+3+......+x+1 个2;

      那 e 之前的所有非2的幂的偶数 even ,该如何快速求出他们的乘积最多能分解出多少个2呢?

      首先,求解一下 2x+1 及其之前的偶数乘积最多可以分解出多少个2;

      首先考虑这一点,任何一个偶数 even 都可分解成 odd*2i 模式, 那么 |Peven | = i;

      那么,我们反过来考虑,对于任意奇数 odd 都可通过 odd*2i 求出 | Podd*2i | = i;

      

          (每两个相邻的2的幂间的偶数块用红色编号① ② ③ ④ ...........表示)

          (紫色数字代表相邻的2的幂间的奇数的个数)

      对于偶数块①:只有一个奇数 3;

      3*21 来到偶数块②;

      3*22 来到偶数块③;

      3*23 来到偶数块④;

      ........

      3*2x-1来到偶数块(x);

      (注意偶数块的编号和2的幂的关系)

      可知,通过3*2可求出P3*21 , P3*22 , .........., P3*2x-1,其集合元素个数总和为 1+2+3+..........+(x-1);

      那么,对于偶数块 2 中的某一奇数 odd 呢?

      根据对3的分析,可知,偶数块②可以构成的 ≤ 2x+1 的最大的偶数为 odd*2x-2 ,那么,通过 odd 求出的集合Podd*2i 的元素个数总和为 1+2+3+.......+(x-2)

      因为偶数块②有21个奇数,所以这些奇数可以求出的集合P的元素个数总和为 2*(1+2+3+........+(x-2) );

      偶数块③有22个奇数,其中任意一个 odd 可构成的 ≤ 2x+1 的最大的偶数为 odd*2x-3 ,这些奇数可以求出的集合P的元素个数总和为 22*(1+2+......+(x-3) );

      ..............

      偶数块(x-1)有2x-2个奇数,其可求出的集合P的元素个数总和为2x-2;

      综上,2x+1 及其之前的偶数乘积最多可以分解出的2的个数为:

      

      这几项加和的值等于 2x+1-1;

      (并不是通过公式化简成这个最简式,而是通过打表找到的,公式化简的话,只能依靠那些大佬了QWQ)

      打表推公式代码:(明天张贴)

     1 #include<iostream>
     2 #include<cstdio>
     3 using namespace std;
     4 
     5 int Sum(int pow,int up)
     6 {
     7     int sum=0;
     8     for(int i=1;i <= up;++i)
     9         sum += i;
    10     return sum*pow;
    11 }
    12 int main()
    13 {
    14     int x;
    15     while(~scanf("%d",&x))
    16     {
    17         int sum=Sum(1,x);
    18         int base=1;
    19         for(int i=1;i <= x-2;++i)
    20         {
    21             sum += Sum(base,x-1-i);
    22             base *= 2;
    23         }
    24         printf("sum=%d,%d
    ",sum,1<<x);
    25     }
    26 }
    View Code

      那么,如果 e 为2的幂,直接输出 e-1;

      如果不是,先求出 ≤ e 的最大的2的幂 2x+1 ,记录当前答案 ans = 2x+1-1;

      那[2x+1 , e]之间的解该如何求出呢?

      还是考虑 odd*2i 的模式;

      令 odd1 ∈ [ 2x+1 / 2 , e / 2],∀odd1 * 2 ∈ [ 2x+1 , e ],且为 e-prime,ans += tot1*1;(tot为奇数个数)

      令 odd2 ∈ [ 2x+1 / 4 , e / 4],∀odd2 * 4 ∈ [ 2x+1 , e ],且为 e-prime,ans += tot2*2;

      .............

      令 oddk ∈ [ 2x+1 / 2k , e / 2k],∀oddk * 2x ∈ [ 2x+1 , e ],且为 e-prime,ans += totk*k;

      结束的条件是  2x+1 / 2k = e / 2k

    AC代码:(C++版大数伪码)(明天张贴)

     1 #include<iostream>
     2 #include<cstdio>
     3 using namespace std;
     4 #define BigInteger long long
     5 
     6 BigInteger e;
     7 BigInteger base;
     8 
     9 BigInteger F()
    10 {
    11     BigInteger curBase=2;
    12     BigInteger ans=0;
    13     BigInteger l=base/curBase;
    14     BigInteger r=e/curBase;
    15     for(int i=1;i < 4000;++i)
    16     {
    17 //        cout<<l<<' '<<r<<endl;
    18         if(l >= r)
    19             break;
    20         BigInteger tmp=(r-l)/2;
    21         if(r&1)
    22             tmp++;
    23         ans=ans+tmp*i;
    24         l=l/2;
    25         r=r/2;
    26     }
    27     return ans;
    28 }
    29 void Solve()
    30 {
    31     base=1;
    32     while(base*2 <= e)
    33         base *= 2;
    34     BigInteger ans=base-1;
    35 
    36     if(base != e)
    37         ans=ans+F();
    38 
    39     cout<<ans<<endl;
    40 }
    41 int main()
    42 {
    43     int test;
    44     while(cin>>test)
    45     {
    46         while(test--)
    47         {
    48             cin>>e;
    49             Solve();
    50         }
    51     }
    52     return 0;
    53 }
    C++版 BigInteger(逃)

    AC代码:(Java BigInteger类)(明天张贴)

     1 import java.math.BigInteger;
     2 import java.util.Scanner;
     3 
     4 public class Main {
     5 
     6     static Scanner cin = new Scanner(System.in);
     7     static BigInteger e,base;
     8     static BigInteger zero=BigInteger.valueOf(0);
     9     static BigInteger one=BigInteger.valueOf(1);
    10     static BigInteger two=BigInteger.valueOf(2);
    11     public static void main(String[] args) {
    12         
    13         int test;
    14         while(cin.hasNext()) {
    15             test=cin.nextInt();
    16             for(int i=1;i <= test;++i) {
    17                 
    18                 e=cin.nextBigInteger();
    19                 Solve();
    20             }
    21         }
    22     }
    23     private static void Solve() {
    24         
    25         base=one;
    26         while(base.multiply(two).compareTo(e) <= 0)
    27             base=base.multiply(two);
    28 //        System.out.println(base);
    29         BigInteger ans=base.subtract(one);
    30         
    31         if(!base.equals(e))
    32             ans=ans.add(F());
    33         System.out.println(ans);
    34     }
    35     private static BigInteger F() {
    36 
    37         BigInteger curBase=two;
    38         BigInteger ans=zero;
    39         BigInteger l=base.divide(curBase);
    40         BigInteger r=e.divide(curBase);
    41         for(int i=1;i < 4000;++i) {
    42             
    43             if(l.compareTo(r) >= 0)
    44                 break;
    45             BigInteger tmp=(r.subtract(l)).divide(two);
    46             if(r.mod(two).intValue() != 0)
    47                 tmp=tmp.add(one);
    48             ans=ans.add(tmp.multiply(BigInteger.valueOf(i)));
    49             
    50             l=l.divide(two);
    51             r=r.divide(two);
    52         }
    53         return ans;
    54     }
    55 }
    View Code

    今晚比较嗨,比较尽兴;

    陪俺家宝宝乘轻轨去市里疯了一圈,返程的路上还做过了一站;

    哈哈哈,要一直陪着宝宝啊*_*

  • 相关阅读:
    CodeForces Gym 100500A A. Poetry Challenge DFS
    CDOJ 486 Good Morning 傻逼题
    CDOJ 483 Data Structure Problem DFS
    CDOJ 482 Charitable Exchange bfs
    CDOJ 481 Apparent Magnitude 水题
    Codeforces Gym 100637G G. #TheDress 暴力
    Gym 100637F F. The Pool for Lucky Ones 暴力
    Codeforces Gym 100637B B. Lunch 找规律
    Codeforces Gym 100637A A. Nano alarm-clocks 前缀和
    TC SRM 663 div2 B AABB 逆推
  • 原文地址:https://www.cnblogs.com/violet-acmer/p/10709026.html
Copyright © 2011-2022 走看看