51nod上一些数论题集合

1130 N的阶乘的长度 V2(斯特林近似)

输入N求N的阶乘的10进制表示的长度。例如6! = 720,长度为3。(1 <= N <= 10^9)

题解:斯特林公式:

大数阶乘求位数,不能用常规方法了,两边同时取log为:(注意后面+1)

log10(n!) = 0.5 * log10(2.0*PI*n) + 1.0*n*log10(1.0*n/e) + 1

#include
#include
#include
using namespace std;
const double pi=3.1415926535898,e=2.718281828459;
int main()
{
    int t;
    scanf("%d",&t);
    for(int i=1;i<=t;i++)
    {
        long long n;
        scanf("%lld",&n);
        long long ans=0.5*log10(2.0*pi*n)+1.0*n*log10(1.0*n/e)+1;
        printf("%lld\n",ans);
    }
    return 0;
}

1058 N的阶乘的长度

输入N求N的阶乘的10进制表示的长度。例如6! = 720,长度为3。(1 <= N <= 10^6)

题解:这个是常规方法,注意不能每步取整,累加后向上取整。

#include 
#include 
#include 
#include 
#include 
using namespace std;

int main() { 
	int n;
	double len=0;
	cin>>n;
    for(int i=1;i<=n;++i){
        len += log10(i);
    }
    cout<<(int)(len+1)<<"\n";
	return 0;
}

1019 逆序数

在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。如2 4 3 1中,2 1,4 3,4 1,3 1是逆序,逆序数是4。给出一个整数序列,求该序列的逆序数。(n <= 50000)(0 <= A[i] <= 10^9)

题解:就是归并排序的使用。

#include 
#include 
using namespace std;
int sum = 0;

int inversionNum(int arr[], int l, int mid, int r){
	int i = l, j = mid + 1, cnt = 0;
	for (int m = i; m <= mid; m++){
		for (int n = j; n <= r; n++){
			if (arr[m] > arr[n]){
				cnt += r - n + 1;
				break;
			}
		}
	}
	return cnt;
}

void mergeSort(int arr[], int l, int mid, int r){
	int *aux = new int[r - l + 1];
	for (int i = l; i <= r; i++)
		aux[i - l] = arr[i];

	int i = l, j = mid + 1;
	for (int k = l; k <= r; k++){
		if (i > mid)
			arr[k] = aux[j - l]; j++;
		else if (j > r)
			arr[k] = aux[i - l]; i++;
		else if (aux[i - l] > aux[j - l])
			arr[k] = aux[i - l]; i++;
		else
			arr[k] = aux[j - l]; j++;
	}
	delete[] aux;	
}

void __merge(int arr[], int l, int r){
	if (l >= r)
		return;

	int mid = (l + r) / 2;
	__merge(arr, l, mid);
	__merge(arr, mid + 1, r);
	sum += inversionNum(arr, l, mid, r);
	mergeSort(arr, l, mid, r);
}

void merge(int arr[], int n)
	__merge(arr, 0, n - 1);

int main(){	
	int n;
	cin >> n;
	int *a = new int[n];
	for (int i = 0; i < n; i++)
		cin >> a[i];
	merge(a, n);
	cout << sum << endl;
	return 0;
}

1006 最长公共子序列Lcs

给出两个字符串A B,求A与B的最长公共子序列(子序列不要求是连续的)。比如两个串为:

abcicba

abdkscab

ab是两个串的子序列,abc也是,abca也是,其中abca是这两个字符串最长的子序列。

状态转移方程:

mat[i][j] = max(mat[i-1][j], mat[i][j-1], mat[i-1][j-1] + (A[i]==B[j] ? 1 : 0));

#include 
#define  MAXN  1002
char A[MAXN] = {0};
char B[MAXN] = {0};
char R[MAXN] = {0};
short mat[MAXN][MAXN] = {0};

//返回三个数的最大值
short max(short a, short b, short c){
    if(a > b)
        b = a;
    return  b > c ? b : c;
}

int main(){
    int  i, j = 0, k;
    scanf("%s %s", A + 1, B + 1);
    for(i = 1; A[i]; i++)
        for(j = 1; B[j]; j++)
            mat[i][j] = max(mat[i-1][j], mat[i][j-1], mat[i-1][j-1] + (A[i]==B[j] ? 1 : 0));
    i--;
    j--;
    k = MAXN - 1;
    while(i > 0 && j > 0){
        if(A[i] == B[j]){
            R[k--] = A[i];
            i--;
            j--;
        }
        else if(mat[i-1][j] > mat[i][j-1])
            i--;
        else
            j--;

    }
    printf( "%s\n", R + k + 1);
    return 0;
}

1049 最大子段和

N个整数组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的连续子段和的最大值。当所给的整数均为负数时和为0。例如:-2,11,-4,13,-5,-2,和最大的子段为:11,-4,13。和为20。

题解:dp[i] = max(A[i], dp[i - 1] + A[i]); k = max(k,dp[i]);

1066 Bash游戏

有一堆石子共有N个。A B两个人轮流拿,A先拿。每次最少拿1颗,最多拿K颗,拿到最后1颗石子的人获胜。假设A B都非常聪明,拿石子的过程中不会出现失误。给出N和K,问最后谁能赢得比赛。例如N = 3,K = 2。无论A如何拿,B都可以拿到最后1颗石子。

题解:n % (k+1) ? 'A' : 'B'

1069 Nim游戏

有N堆石子。A B两个人轮流拿,A先拿。每次只能从一堆中取若干个,可将一堆全取走,但不可不取,拿到最后1颗石子的人获胜。假设A B都非常聪明,拿石子的过程中不会出现失误。给出N及每堆石子的数量,问最后谁能赢得比赛。例如:3堆石子,每堆1颗。A拿1颗,B拿1颗,此时还剩1堆,所以A可以拿到最后1颗石子。

题解:知乎上SimonS关于Nim博弈的回答!

1072 威佐夫游戏

有2堆石子。A B两个人轮流拿,A先拿。每次可以从一堆中取任意个或从2堆中取相同数量的石子,但不可不取。拿到最后1颗石子的人获胜。假设A B都非常聪明,拿石子的过程中不会出现失误。给出2堆石子的数量,问最后谁能赢得比赛。例如:2堆石子分别为3颗和5颗。那么不论A怎样拿,B都有对应的方法拿到最后1颗。

题解:转载:http://blog.csdn.net/update7?viewmode=contents https://blog.csdn.net/update7/article/details/76450523

/*
Wythoff Game:黄金分割
先取完者赢
威佐夫博弈:每次可以从一堆中取任意个或从2堆中取相同数量的石子,但不可不取
一个局面,让你求是先手输赢:差值 * 1.618 == 最小值的话后手赢,否则先手赢
一个局面,让你求先手输赢,假设先手赢的话输出他第一次的取法:
首先讨论在两边同时取的情况,很明显两边同时取的话,不论怎样取他的差值是不会变的,那么我们可以根据差值计算出其中的小的值,然后加上差值就是大的一个值,当然能取的条件是求出的最小的值不能大于其中小的一堆的石子数目。
加入在一堆中取的话,可以取任意一堆,那么其差值也是不定的,但是我们可以枚举差值,差值范围是0----大的石子数目,然后根据上面的理论判断满足条件的话就是一种合理的取法。
*/
#include
#include
#include
using namespace std;
double g=(sqrt(5)+1)/2;
int main(){
	int n,a,b;
	scanf("%d",&n);
	while(n--)
	{
		scanf("%d%d",&a,&b);
		if(a>b) swap(a,b);
		int t=(b-a)*g;
		if(t==a) puts("B");
		else puts("A");
	}
	return 0;
}

1073 约瑟夫环

N个人坐成一个圆环(编号为1 - N),从第1个人开始报数,数到K的人出列,后面的人重新从1开始报数。问最后剩下的人的编号。例如:N = 3,K = 2。2号先出列,然后是1号,最后剩下的是3号。

a[1] = 0;
for (i = 1; i < n; i++)
    a[i + 1] = (a[i] + k) % (i + 1);
cout << a[i] + 1 << endl;

1079 中国剩余定理

一个正整数K,给出K Mod 一些质数的结果,求符合条件的最小的K。例如,K % 2 = 1, K % 3 = 2, K % 5 = 3。符合条件的最小的K = 23。

题解:https://blog.csdn.net/Dafang_Xu/article/details/50818919

# include 
# include 
# define ll long long
using namespace std;

struct Record{
	int a;
	int b;
}record[20];

void gcd(ll a, ll b, ll &d, ll &x, ll &y){
	if ( b==0 ){d = a;x = 1;y = 0;}
	else{gcd(b, a%b, d, y, x);y -= (a/b)*x;}
}

int main(){
	ll n, c = 0;
	ll d, y, w;
	ll mul = 1;
	ll m;
	cin >> n;
	for ( int i = 1 ; i <= n ; i ++ ){
		cin >> record[i].a >> record[i].b;
		mul *= record[i].a;
	}
	for ( int i = 1 ; i <= n ; i ++ ){
 		w = mul / record[i].a;
		gcd(record[i].a, w, d, d, y);
		c = (c + y * w * record[i].b) % mul;
	}
	cout << (c + mul) % mul << endl;	
	return 0;
} 

未完待续。。

你可能感兴趣的:(数论)