UVA1635 Irrelevant Elements(唯一分解定理 + 组合数递推)

http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=51196

紫书P320;

题意:给定n个数a1,a2····an,依次求出相邻两个数值和,将得到一个新数列,重复上述操作,最后结果将变为一个数,问这个数除以m的余数与那些数无关?例如n=3,m=2时,第一次得到a1+a2,a2+a3,在求和得到a1+2*a2+a3,它除以2的余数和a2无关。1=<n<=10^5, 2=<m<=10^9

 其实就是杨辉三角的某一行有几个能整除m,求C(0,n - 1),C(1, n - 2)... C(n - 1, n - 1)中那几个能整除m

 

解题思路:

1、首先我们可以发现对于给定的n其实每项的系数就是C(n-1,i-1),所以我们只需要找到每项的系数对m取余是否为0即可

2、由于m的取值范围为10^9,所以我们只需要筛选 √(10^9)的素数,然后对m进行分解;如果分解后m>1,说明当前m的是原m的一个素数,而且m> √(10^9),因此我们只需记录它即可

3、由组合数的递推公式C(k, n) = (n - k + 1) / k * C(k - 1, n),而这道题n = n - 1;首项是一,可以直接从第二项即k = 1: (n - 1 )  - k + 1 / k开始判断是否能整除m,分子能整除素数元素个数就减一,分母能整除就+1,如果对于任意一个素数对应的指数 >= 1,就说明不能整除

收获:

每个整数的唯一分解式项数不多(long long 类型的数值最多20项,前21个素数相乘long long就溢出了)

  1 #include <iostream>
  2 #include <algorithm>
  3 #include <cstdio>
  4 #include <cstring>
  5 using namespace std;
  6 const int Max = 40000;
  7 
  8 int factor[20],nfactor[20],num_factor,flag_factor[100000 + 10];
  9 int count_prime,prime[Max + 5],flag[Max + 5];
 10 void get_prime()
 11 {
 12     memset(flag, 0, sizeof(flag));
 13     count_prime = 0;
 14     for(int i = 2; i <= Max; i++)
 15     {
 16         if(flag[i] == 0)
 17         {
 18             flag[i] = 1;
 19             prime[++count_prime] = i;
 20             for(int j = 2; j <= Max / i; j++)
 21             {
 22                 flag[i * j] = 1;
 23             }
 24         }
 25     }
 26 }
 27 
 28 void get_factor(int m)
 29 {
 30     num_factor = 1;
 31     memset(nfactor, 0, sizeof(nfactor));
 32     memset(factor, 0, sizeof(factor));
 33     for(int i = 1; i <= count_prime; i++)
 34     {
 35         if(m < prime[i])
 36             break;
 37         if(m % prime[i] == 0)
 38         {
 39             factor[num_factor] = prime[i];
 40             while(m % prime[i] == 0 && m)
 41             {
 42                 nfactor[num_factor]++;
 43                 m = m / prime[i];
 44             }
 45             num_factor++;
 46             if(m == 0 || m == 1)
 47                 break;
 48         }
 49     }
 50     if(m > 1)
 51     {
 52         factor[num_factor] = m;
 53         nfactor[num_factor]++;
 54         num_factor++;
 55     }
 56 }
 57 bool check(int x, int y)
 58 {
 59     bool check_flag = true;
 60     for(int i = 1; i < num_factor; i++)
 61     {
 62         while(x % factor[i] == 0 && x)
 63         {
 64             x = x / factor[i];
 65             nfactor[i]--;
 66         }
 67         while(y % factor[i] == 0 && y)
 68         {
 69             y = y / factor[i];
 70             nfactor[i]++;
 71         }
 72         if(nfactor[i] >= 1)
 73         {
 74             check_flag = false;
 75             //break;  要不得,即使不能整除,也要约分完,因为下一个受这一个的影响
 76         }
 77     }
 78     return check_flag;
 79 }
 80 int main()
 81 {
 82     int n,m;
 83     get_prime();
 84     while(scanf("%d%d", &n, &m) != EOF)
 85     {
 86         get_factor(m);  //对m分解
 87         int cnt = 0, ends = 0;
 88         memset(flag_factor, 0, sizeof(flag_factor));
 89         for(int i = 1; i <= n; i++)
 90         {
 91             if( check(n - i, i) )   //原型就是 (n - 1 - i + 1) / i
 92             {
 93                 flag_factor[i + 1] = 1;
 94                 ends = i + 1;
 95                 cnt++;
 96             }
 97         }
 98         printf("%d\n", cnt);
 99         if(cnt > 0)
100         {
101             for(int i = 1; i < ends; i++)
102             if(flag_factor[i])
103                 printf("%d ", i);
104             printf("%d", ends);
105         }
106         printf("\n");
107     }
108     return 0;
109 }
View Code

 

转载于:https://www.cnblogs.com/zhaopAC/p/5203582.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值