2019.01.19【雅礼集训2019Day1T3】math(矩阵快速幂)

描述

给出 n, m, x,你需要求出下列式子的值:
∑ ∑ i = 1 m k i = n ∏ i = 1 m sin ⁡ ( k i ∗ x ) \sum_{\sum_{i=1}^{m}k_i=n}\prod_{i=1}^m\sin(k_i*x) i=1mki=ni=1msin(kix)
其中ki为正整数,由于答案非常大,你只需要输出答案(保证不为 0)的正负(如果是负数输出负号,否则输出正号)和从左往右第一个非 0 数位上的数字即可。

输入

第一行一个整数 T 表示数据组数。

对于每组数据,每行有两个整数 m,n 和一个两位小数 x。第一行一个整数 T 表示数据组数。

对于每组数据,每行有两个整数 m,n 和一个两位小数 x。

输出

输出共 T 行,每行两个字符表示答案。

样例输入

2
3 5 0.01
3 6 0.02

样例输出

+2
+4

提示

对于 10%的数据, n,m≤5

对于 30%的数据, n≤500,m≤10

对于 50%的数据, n≤10000

对于 100%的数据, T≤10, n ≤ 1e9 ,m≤30


解析:

没想到吧,这道题有隐含的递推关系。

首先要知道 sin ⁡ \sin sin cos ⁡ \cos cos K K K倍角公式:
∀ K ∈ N ∗ , x ∈ R , \forall K\in \mathbb{N_*},x\in \mathbb{R}, KN,xR, sin ⁡ ( K ∗ x ) = sin ⁡ ( x ) cos ⁡ ( ( K − 1 ) ∗ x ) + cos ⁡ ( x ) sin ⁡ ( ( K − 1 ) ∗ x ) \sin(K*x)=\sin(x)\cos((K-1)*x)+\cos(x)\sin((K-1)*x) sin(Kx)=sin(x)cos((K1)x)+cos(x)sin((K1)x) cos ⁡ ( K ∗ x ) = cos ⁡ ( x ) cos ⁡ ( ( K − 1 ) ∗ x ) − sin ⁡ ( x ) sin ⁡ ( ( K − 1 ) ∗ x ) \cos(K*x)=\cos(x)\cos((K-1)*x)-\sin(x)\sin((K-1)*x) cos(Kx)=cos(x)cos((K1)x)sin(x)sin((K1)x)

证明可以看这里:https://blog.csdn.net/zxyoi_dreamer/article/details/85956203

然后我们可以开始强行推式子了。

推导:

首先刚才提到的 K K K倍角公式是要用的。这里先写上:
sin ⁡ ( K ∗ x ) = sin ⁡ ( x ) cos ⁡ ( ( K − 1 ) ∗ x ) + cos ⁡ ( x ) sin ⁡ ( ( K − 1 ) ∗ x ) \sin(K*x)=\sin(x)\cos((K-1)*x)+\cos(x)\sin((K-1)*x) sin(Kx)=sin(x)cos((K1)x)+cos(x)sin((K1)x)

但是令人窒息的是我们还有一个 cos ⁡ ( ( K − 1 ) ∗ x ) \cos((K-1)*x) cos((K1)x)需要处理。

作如下推导:
sin ⁡ ( x ) cos ⁡ ( K ∗ x ) = sin ⁡ ( x ) ( cos ⁡ ( x ) cos ⁡ ( ( K − 1 ) ∗ x ) − sin ⁡ ( x ) sin ⁡ ( ( K − 1 ) ∗ x ) ) = sin ⁡ ( x ) cos ⁡ ( x ) cos ⁡ ( ( K − 1 ) ∗ x ) + ( cos ⁡ 2 ( x ) − 1 ) sin ⁡ ( ( K − 1 ) ∗ x ) = cos ⁡ ( x ) ( sin ⁡ ( x ) cos ⁡ ( ( K − 1 ) ∗ x ) + cos ⁡ ( x ) sin ⁡ ( ( K − 1 ) ∗ x ) ) − sin ⁡ ( ( K − 1 ) ∗ x ) = cos ⁡ ( x ) sin ⁡ ( K ∗ x ) − s i n ( ( K − 1 ) ∗ x ) \begin{aligned} \sin(x)\cos(K*x)=&\sin(x)(\cos(x)\cos((K-1)*x)-\sin(x)\sin((K-1)*x))\\ =&\sin(x)\cos(x)\cos((K-1)*x)+(\cos^2(x)-1)\sin((K-1)*x) \\ =&\cos(x)(\sin(x)\cos((K-1)*x)+\cos(x)\sin((K-1)*x))-\sin((K-1)*x)\\ =&\cos(x)\sin(K*x)-sin((K-1)*x) \end{aligned} sin(x)cos(Kx)====sin(x)(cos(x)cos((K1)x)sin(x)sin((K1)x))sin(x)cos(x)cos((K1)x)+(cos2(x)1)sin((K1)x)cos(x)(sin(x)cos((K1)x)+cos(x)sin((K1)x))sin((K1)x)cos(x)sin(Kx)sin((K1)x)

所以容易得到 sin ⁡ ( K ∗ x ) = 2 cos ⁡ ( x ) sin ⁡ ( ( K − 1 ) ∗ x ) − sin ⁡ ( ( K − 2 ) ∗ x ) \sin(K*x)=2\cos(x)\sin((K-1)*x)-\sin((K-2)*x) sin(Kx)=2cos(x)sin((K1)x)sin((K2)x)

我们现在已经确定了 x x x,设 f ( n , m ) f(n,m) f(n,m)表示 n , m n,m n,m为对应值的时候的答案。

考虑现在我们需要知道 f ( n 0 , m 0 ) f(n_0,m_0) f(n0,m0)的答案。

我们只针对 k m 0 k_{m_0} km0不同讨论不同的转移:
1. k m 0 = 1 k_{m_0}=1 km0=1,显然贡献为 f ( n 0 − 1 , m 0 − 1 ) × sin ⁡ ( x ) → f ( n 0 , m 0 ) f(n_0-1,m_0-1)\times \sin(x)\rightarrow f(n_0,m_0) f(n01,m01)×sin(x)f(n0,m0)
2. k m 0 > 1 k_{m_0}>1 km0>1,此时所有序列的贡献为 2 ∗ cos ⁡ ( x ) f ( n 0 − 1 , m 0 ) − f ( n 0 − 2 , m 0 ) → f ( n 0 , m 0 ) 2*\cos(x)f(n_0-1,m_0)-f(n_0-2,m_0)\rightarrow f(n_0,m_0) 2cos(x)f(n01m0)f(n02,m0)f(n0,m0)

所以我们需要维护一个 ( 2 m ) ∗ ( 2 m ) (2m)*(2m) (2m)(2m)的矩阵,为了同时维护前两次转移出的矩阵,直接按照递推关系构造矩阵快速幂就可以了。


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline int getint(){
	re int num;
	re char c;
	while(!isdigit(c=gc()));num=c^48;
	while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
	return num;
}

inline double getdb(){
	re double x,y=1.0;
	re char c;
	re bool f=0;
	while(!isdigit(c=gc()))if(c=='-')f=1;x=c^48;
	while(isdigit(c=gc()))x=(x*10)+(c^48);
	if(c!='.')return f?-x:x;
	while(isdigit(c=gc()))x+=(y/=10)*(c^48);
	return f?-x:x;
}

cs int M=62;
int msiz;
struct matrix{
	double a[M][M];
	matrix(){memset(a,0,sizeof a);}
	inline cs double *cs operator[](cs int &offset)cs{return a[offset];}
	inline double *cs operator[](cs int &offset){return a[offset];}
	inline friend matrix operator*(cs matrix &A,cs matrix &B){
		matrix C;
		for(int re i=1;i<=msiz;++i)
		for(int re j=1;j<=msiz;++j)if(fabs(A[i][j])>1e-6)
		for(int re k=1;k<=msiz;++k)
		C[i][k]+=A[i][j]*B[j][k];
		return C;
	}
}A,B;

inline matrix quickpow(matrix a,int b,matrix res){
	for(;b;b>>=1,a=a*a)if(b&1)res=res*a;
	return res;
}

int T;
int n,m;
double x;
signed main(){
	T=getint();
	while(T--){
		m=getint();
		n=getint();msiz=m<<1;
		x=getdb();
		A=matrix();B=matrix();
		for(int re i=m+1;i<=m*2;++i){
			A[i][i-m]=1;
			A[i-m][i]=-1;
			A[i][i]=2*cos(x);
			if(i<m*2)A[i][i+1]=sin(x);
		}
		B[1][1]=sin(x);
		B[1][m+1]=sin(2*x);
		B[1][m+2]=sin(x)*sin(x);
		B=quickpow(A,n-1,B);
		double res=B[1][m];
		if(res<0)pc('-'),res=-res;
		else pc('+');
		while(res<1)res*=10;
		while(res>=10)res/=10;
		pc(48^(int)floor(res));pc('\n');
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值