HDU 3483 二项式展开+矩阵快速幂

A Very Simple Problem

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 1126    Accepted Submission(s): 554


Problem Description
This is a very simple problem. Given three integers N, x, and M, your task is to calculate out the following value:


 

Input
There are several test cases. For each case, there is a line with three integers N, x, and M, where 1 ≤ N, M ≤ 2*10 9, and 1 ≤ x ≤ 50.
The input ends up with three negative numbers, which should not be processed as a case.
 

Output
For each test case, print a line with an integer indicating the result.
 

Sample Input
  
  
100 1 10000 3 4 1000 -1 -1 -1
 

Sample Output
  
  
5050 444
 
系数矩阵详见代码:

/* 
Sn=1^x * x^1 + 2^x * x^2 +...+ n^x * x^n; 
Sn+1=1^x * x^1 + 2^x * x^2 +...+ n^x * x^n+(n+1)^x * x^(n+1)
=Sn+(n+1)^x * x^(n+1),将(n+1)^x二项式展开然后用矩阵快速幂
构造矩阵: 
|1 xC(x,0) xC(x,1) xC(x,2) ... xC(x,x)|  |Sn       | |S(n+1)           | 
|0 xC(x-1,0)    ......  xC(x-1,x-1)   |  |x^n * n^x| |x^(n+1) * (n+1)^x| 
|0  0	xC(x-2,0)...... xC(x-2,x-2)   | *|...      |=|...	           | 
|...				                  |  |x^n * n^2| |x^(n+1) * (n+1)^2| 
|...                                  |  |x^n * n^1| |x^(n+1) * (n+1)^1| 
|0	0	0    0	    xC(0,0)           |  |x^n * n^0| |x^(n+1) * (n+1)^0| 
*/ 
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

long long c[51][51] ,N,M;
int x;
struct Node {
	long long M[55];
}res;
struct Node_a {
	long long M[55][55];
}ori;
void calc_comb()	//组合数打表 
{
	c[0][0] = c[1][0] = c[1][1]=1;
	for(int i=2;i<51;i++)
	{
		c[i][0] = c[i][i] = 1;
		for(int j=1;j<i;j++)
			c[i][j] = (c[i-1][j]+c[i-1][j-1])%M;
	}
}
Node mul(Node a,Node_a b)	//系数矩阵自乘
{
	Node tem;
	memset(tem.M,0,sizeof(tem.M));
	for(int i=0;i<x+2;i++)
		for(int j=0;j<x+2;j++)
			tem.M[i]=(tem.M[i]+ori.M[i][j]*res.M[j])%M;
	return tem;
}
Node_a mul1(Node_a a ,Node_a b)	
{
	Node_a tem;
	memset(tem.M,0,sizeof(tem.M));
	for(int i=0;i<x+2;i++)
		for(int j=0;j<x+2;j++)
			for(int k=0;k<x+2;k++)
				tem.M[i][j]=(tem.M[i][j]+a.M[i][k]*b.M[k][j])%M;//防止越界 
	return tem;
}
void calc()
{
	memset(ori.M,0,sizeof(ori.M));	ori.M[0][0]=1;
	for(int i=1;i<=x+1;i++)	//初始化系数矩阵第一行 
		ori.M[0][i]=(c[x][i-1]*x)%M;
	for(int i=1;i<x+2;i++)	//初始化第二行,往后 
		for(int j=i;j<x+2;j++)
			ori.M[i][j]=(c[x+1-i][j-i]*x)%M;
	for(int i=0;i<x+2;i++)
		res.M[i]=x;
	N--;
	while(N)
	{
		if(N&1)
			res=mul(res,ori);
		N>>=1;
		ori=mul1(ori,ori);
	}
}
int main()
{
	while(scanf("%lld%d%lld",&N,&x,&M))
	{
		if(N==-1 && M==-1 && x==-1)
			break;
		calc_comb();
		calc();
		printf("%lld\n",res.M[0]%M);
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值