//给出一个这样的除法表达式:X1/X2/X3/…/Xk,其中Xi是正整数。除法表达式应当按照从左到右的顺序求和。
// 例如表达式/2/1/2的值为/4。但可以在表达式中嵌入括号以改变计算顺序,例如表达式(1/2)/(1/2)的值为。
//输入:X1,X2,X3,…,Xk,判断是否可以通过添加括号,使表达式的值为整数。k≤,Xi≤。
#include<iostream>
using namespace std;
int main()
{
__int64 n,m,t;
char c;
while(scanf("%I64d",&n)!=EOF)
{
c=getchar();
if(c!='/')
{
cout<<"YES"<<endl;
continue;
}
scanf("%I64d",&t);
while(c=getchar()=='/')
{
n=n%t;
scanf("%I64d",&m);
n*=m;
}
if(n%t==0)cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}
//#include<stdio.h>
//#include<iostream>
//#include<algorithm>
//using namespace std;
//int main()
//{
//char s[3]={'1','2','3'};
//char *p=s;
//while(p!=NULL)
//{
//printf("%c\n",*(p++));
//}
//return 0;
//}
//10.2.4离散概率初步
//概率有一套很深的理论,很多和概率相关的问题并不需要特别的知识,熟悉排列组合就可以了。
//第个例子:连续抛次硬币,恰好有两次正面的概率有多大?用H和T表示正面和背面,
//则一共有种可能的情况:HHH、HHT、HTH、HTT、THH、THT、TTH、TTT。这种情况出现的可能性相同,
//概率各为/8。{HHH、HHT、HTH、HTT、THH、THT、TTH、TTT}称为样本空间(Sample Space)。
//所求的是“恰好有两次正面”这个事件(Event)的概率。这个事件可以表示为{HHT、HTH、THH},
//它的概率是/8。
//#include<iostream>
//using namespace std;
//double brithday(int year,int numpeople)
//{
//double ans=1.0;
//for(int i=0;i<numpeople;i++)
//ans=ans*(year-i)/year;
//return 1-ans;
//}
//int main()
//{
//cout<<brithday(365,23);
//return 0;
//}
.3.5 统计n-k特殊集的数目
如果由正整数构成的集合X满足以下条件,我们称它为n-k特殊集:
()集合X中的每个元素x均不超过n,即≤x≤n。
()集合X中所有元素之和大于k。
()集合X中不包含任意一对相邻的自然数。
给出n,k,求n-k特殊集合有多少个。≤n≤,≤k≤。
样例输入:3
样例输出:
样例输入:55
样例输出:
样例输入:100
样例输出:
【分析】
设n-k特殊集的个数为f(n,k),下面来建立它的递推式。由于集合中的元素不能重复,元素n要么在集合中恰好出现一次,要么不出现,二者不重复、不遗漏的把n-k特殊集分成了两部分:
()n不出现。除了规则()中的n需要修改为n-1外,其他均不变,因此有f(n-1,k)个。
()n出现一次。除了规则()中的n需要修改为n-2外(如果出现了n-1,则相邻自然数n-1、n不满足条件,而规则()中的k应变为k-n。
所以递推关系是:f(n,k)= f(n-1,k)+f(n-2,k-n)。接下来,来求边界。n≤时只有空集一个集合满足规则(),而此时所有元素和为,因此:
()当n≤,k≥时,f(0,k)=0。
()当n≤,k<0时,f(0,k)=1。
在其他情况下,f均能按此递推式计算。但是有一个问题:如果用f[n][k]保存f(n,k)的值,n和k需要允许负数。
//
//#include<iostream>
//#define F(i,j) (f[(i+1)][(j)+1])//用宏处理支持负数下标
//using namespace std;
//
//long long f[200][500];//结果可能很大,需要高精度
//int main()
//{
//int i,j,n,k;
//cin>>n>>k;
//for(j=-1;j<=k;j++)
//F(-1,j)=F(0,j)=0;
//F(-1,-1)=F(0,-1)=1;
//for(i=1;i<=n;i++)
//{
//for(j=-1;j<=k;j++)//递推
//{
//F(i,j)=F(i-1,j);
//if(j-i<0)F(i,j)+=F(i-2,-1);
//else F(i,j)+=F(i-2,j-i);
//}
//}
//cout<<F(n,k)<<endl;
//return 0;
//}
//例-1 大整数取模。
//输入正整数n和m,输出n mod m的值。n≤,m≤。
//【分析】首先,把大整数写成“自左向右”的形式:=((1*10+2)*10+3)*10+4,
//然后用前面的公式(a+b)%m=(a%m+b%m)%m,每步取模,
//程序如下:
//
//#include<cstdio>
//#include<iostream>
//#include<string>
//#include<cstdlib>
//using namespace std;
//int main()
// {
// char n[100];
// int m;
// scanf("%s%d",&n,&m);
// int len=strlen(n);
// int ans=0;
// for(int i=0; i < len; i++)
//
// ans=(int)((long long)((ans*10)%m+n[i]%m)%m);
// /*ans = (int)(((long long)ans*10 + n[i])%m);*///也可以用这种方法取模,从最高位到最低位,每次取模了之后将余数乘以和下一个数相加继续取模,
// //重复这个步骤,就好像做除法是一样的,只不过是分开了。
// printf("%d\n",ans);
// return 0;
// }
输入正整数a、m和n,输出a^n mod m的值。a,n,m≤。
分析:首先很容易想到ab%m=(a%m)(b%m)%m
可写出代码如下:
#include<iostream>
using namespace std;
int main()
{
int a,n=4,m=5;
/*cin>>a>>n>>m;*/
for(a=3;n<20;n++)
{
int ans=1;
for(int i=0;i<n;i++)
{
ans=(int)(long long)ans*a%m;
}
cout<<"asn="<<ans<<endl;
}
return 0;
}
但是对于以上代码,当n比较大的时候就效率就变的很慢了,下面介绍分治法对效率进行提高
其实就是a^2i+1=a^2i*a.例:a^29=(a^14)^2*a,a^14=(a^7)^2,a^7=(a^3)^2*a,a^3=(a)^2*a.
所以这个地方只需要计算n此时的值是奇还是偶,然后决定是否乘以a,其他的直接在ans=a*a的基础上递归平方。即近似少算了/2的乘法运算
//#include<iostream>
//using namespace std;
//int pow_mod(int a,int n,int m)
//{
//if(n==0)return a;
//int x=pow_mod(a,n/2,m);
//long long ans=(long long)x*x%m;
//if(n%2==1)
//ans=ans*a%m;
//return (int)ans;
//}
//int main()
//{
//int a,n=4,m=5;
///*cin>>a>>n>>m;*/
//for(a=3;n<20;n++)
//{
//cout<<"asn="<<pow_mod(a,n,m)<<endl;
//}
//return 0;
//}
例-4 无关的元素。
对于给定的n个数a1,a2,…,an,依次求出相邻两数之和,将得到一个新数列。
重复上述操作,最后结果将变成一个数。问这个数除以m的余数一定与哪些数无关?
例如n=3,m=2时,第一次求和得到a1+a2,a2+a3,再求和得到a1+2a2+a3,它除以的余数和a2无关。
1≤n≤,≤m≤。
【分析】
最后的求和式是a1,a2,…,an的线性组合。设ai的系数为f(i),则和式除以m的余数与ai无关当且仅当f(i)是i的倍数。下面来看一个例子:
a1 a2 a3 a4 a5
a1+a2 a2+a3 a3+a4 a4+a5
a1+2a2+a3 a2+2a3+a4 a3+2a4+a5
a1+3a2+3a3+a4 a2+3a3+3a4+a5
a1+4a2+6a3+4a4+a5
看到最后的结果,“4 6 4 1”正是杨辉三角的第行。在一般情况下,最后ai的系数是Cn-1^(i-1)。问题就变成了:二项式系数中有哪些是m的倍数。
//#include<iostream>
//using namespace std;
//const int N=1000;
//int main()
//{
//int a[N];//用来存放输入的数据
//int C[N];//用来记录二项式的值
//int num;
//cout<<"请输入a数组元素个数:";
//cin>>num;
// /*cout<<"请输入a数组元素:";//不需要输入数组元素
//for(int i=0;i<num;i++)
//cin>>a[i];*/
// int m;
//cout<<"请输入m:";
//cin>>m;
//
// C[0]=num-1;
//for(int i=1;i<num;i++)
//{
//C[i]=C[i-1]*(num-i)/i;//原本是(num-1-i+1)
//}
//for(int i=0;i<num;i++)
//{
//if(C[i]%m==0)cout<<"无关的数是第:"<<i+1<<"个。"<<endl;
//}
//return 0;
//}
例-5 约数的个数。
给出正整数n的唯一分解式,求n的正约数的个数。
【分析】
下面先给出一个引理-1,描述如下:
引理-1 大于的自然数都可以分解为素数的乘积。
定理-16(唯一分解定理)任何一个大于自然数n的唯一分解式
其中p1,p2,…,pk是n的素数因子且p1<p2<…<pk,a1,a2,…,ak是非负整数,k≥。
定理-17(约数个数定理)任何大于自然数n可以分解成标准分解式,则n的正约数个数为:(a1+1)(a2+1)...(ak+1)
//
//#include <stdio.h>
//#include <math.h>
//const int N = 1000;
//int e[N]={0}; //e[N]存放素因子的指数
//
//int main() {
// int n,i,total = 1,tn;
// int sum=0;
// scanf("%d", &n); //输入一个大于的自然数n
// printf("%d=", n);
// tn=n;
// for(i = 2; i*i <= n; i++) //试除~sqrt(n)
// while(n != i){
// if(n%i == 0)//这个地方,可谓是在n的范围内把能被i正处的前面的素数的倍数都去掉了,
// {
// printf("%d*",i); //边计算边输出
// e[i]++; //素因子i对应的指数加
// n = n/i; //对应的n应为n/i
// }
// else break;
//}
注意这个n才是最后一个质因数,最后程序结束时的n是最大的那个质因数
// e[n]++;
// printf("%d\n", n);
// //输出每个素数因子的个数,另一种形式
//printf("%d=", tn);
// for(i = 1; i < N; i++) //输出每个素数因子的个数
// if(e[i]) //如果i是n的素因子,则e[i]必不为
// printf("%d(%d)",i,e[i]);
// printf("\n",n);
// for(i = 1; i < N; i++) //输出n正约数的个数
// if(e[i]) {
//sum = sum + (1 -pow((double)i,e[i]+1))/(1-i); //用等比数列公式求所有正约数之和
//total = total*(e[i]+1);//根据公式,正约数的个数等于每一项的指数加相乘。
// }
// printf("%d\n", sum);
// printf("%d\n", total);
// return 0;
//}
例-6 小于n且与n互素的个数。
给出正整数n的唯一分解式,求,2,3,…,n中与n互素的数的个数。
互素的数的个数可由公式得到:num(n)=n(1-1/p1)(1-1/p2)...(1-1/pk);
#include<iostream>
using namespace std;
int main()
{
int n;
cin>>n;
int ans=n;
for(int i=2;i*i<n;i++)
{
if(n%i==0)ans=ans/i*(i-1);
while(n%i==0)n/=i;//将~n中i的倍数全部清除掉直到n对i取余不为为止
}
if(n>1)//此时是最后一个数。如果n是大于的,说明最后一个n只能被自己整除了,所以有以下代码
ans=ans/n*(n-1);
cout<<"与n互素的个数是:"<<ans<<endl;
return 0;
}
//
//
求出~n中所有数的欧拉函数值
//#include<iostream>
//#include<cmath>
//#include<cassert>
//#include<cstring>
//using namespace std;
//int phi[8000001];
//int main(){
//int n;
//cin >> n;
//assert(n <= 8000000);
//memset(phi, 0, sizeof(phi));
//phi[1] = 1; //O(nloglogn)
//for(int i = 2; i <= n; ++i)
//if(!phi[i])//只算质数i,因为合数i对应的phi[i]已经被前面的质数i在内层j循环中筛完了
//{
//for(int j = i; j <= n; j+=i){
//if(!phi[j]) phi[j] = j;//为每个phi[j]先初始化为j
//phi[j] = phi[j] / i * (i-1);//由i(i需要是质数的i)不断的循环对phi[j]进行运算
//}
//}
//for(int i = 1; i <= n; ++i) printf("phi(%d) = %d\n", i, phi[i]);
//while(1);
//return 0;
//}
//
例-6 小于n且与n互素的个数。
给出正整数n的唯一分解式,求,2,3,…,n中与n互素的数的个数。
互素的数的个数可由公式得到:num(n)=n(1-1/p1)(1-1/p2)...(1-1/pk);
#include<iostream>
using namespace std;
int main()
{
int n;
cin>>n;
int ans=n;
for(int i=2;i*i<n;i++)
{
if(n%i==0)ans=ans/i*(i-1);
while(n%i==0)n/=i;//将~n中i的倍数全部清除掉直到n对i取余不为为止
}
if(n>1)//此时是最后一个数。如果n是大于的,说明最后一个n只能被自己整除了,所以有以下代码
ans=ans/n*(n-1);
cout<<"与n互素的个数是:"<<ans<<endl;
return 0;
}
//
//
求出~n中所有数的欧拉函数值
//#include<iostream>
//#include<cmath>
//#include<cassert>
//#include<cstring>
//using namespace std;
//int phi[8000001];
//int main(){
//int n;
//cin >> n;
//assert(n <= 8000000);
//memset(phi, 0, sizeof(phi));
//phi[1] = 1; //O(nloglogn)
//for(int i = 2; i <= n; ++i)
//if(!phi[i])//只算质数i,因为合数i对应的phi[i]已经被前面的质数i在内层j循环中筛完了
//{
//for(int j = i; j <= n; j+=i){
//if(!phi[j]) phi[j] = j;//为每个phi[j]先初始化为j
//phi[j] = phi[j] / i * (i-1);//由i(i需要是质数的i)不断的循环对phi[j]进行运算
//}
//}
//for(int i = 1; i <= n; ++i) printf("phi(%d) = %d\n", i, phi[i]);
//while(1);
//return 0;
//}
//