Math(CC PARSIN)

题目大意

给出m,n和一个两位小数x。
k[1]+k[2]+...+k[m]=nΠmi=1sin(k[i]x)
n<=10^9,m<=30

这题真的是道数学题

首先考虑一个简单的dp
设f[i][j]表示 i=jl=1k[l] 时所有情况的和
容易想到一个简单的转移
f[i][j]=ik=1f[il][j1]sin(kx)
考虑当k[m]=1时对于f[n][m]的贡献为f[n-1][m-1]*sin(x)
当k[m]>1时呢
解数学题时间:
要用到几个常见的公式:
sin(a+b)=sin(a)cos(b)+sin(b)*cos(a)
cos(a+b)=cos(a)cos(b)-sin(a)sin(b)
sin(a)^2+cos(a)^2=1
然后就可以手推了。。。
1sin(kx)=sin((k1)x+x)=sin((k2)x+2x)
122sin(kx)=sin((k1)x+x)+sin((k2)x+2x)
3sin((k1)x+x)=sin(x)cos((k1)x)+cos(x)sin((k1)x)
4sin((k2)x+2x)=2sin(x)cos(x)cos((k2)x)+(cos(x)2sin(x)2)sin((k2)x)
=2sin(x)cos(x)cos((k2)x)+2cos(x)2sin((k2)x)+sin((k2)x)
5sin(x)cos((k1)x)=sin(x)cos((k2)x+x))=sin(x)(cos((k2)x)cos(x)sin((k2)x)sin(x))
6cos(x)sin((k1)x)=cos(x)sin((k2)x+x)=cos(x)(sin(x)cos((k2)x)+cos(x)sin((k2)x))
3,5,67sin((k1)x+x)=sin(x)cos((k1)x)+cos(x)sin((k1)x)
=sin(x)(cos((k2)x)cos(x)sin((k2)x)sin(x))+cos(x)(sin(x)cos((k2)x)+cos(x)sin((k2)x))
=2sin(x)cos(x)cos((k2)x)+cos(x)2sin((k2)xsin(x)2sin((k2)x))
=2sin(x)cos(x)cos((k2)x)+2cos(x)2sin((k2)x)sin((k2)x)
2,4,782sin(kx)=sin((k1)x+x)+sin((k2)x+2x)
=4sin(x)cos(x)cos((k2)x)+4cos(x)2sin((k2)x)+2sin((k2)x)
89sin(kx)=2sin(x)cos(x)cos((k2)x)+2cos(x)2sin((k2)x)+sin((k2)x)
=2cos(x)(sin(x)cos((k2)x)+cos(x)sin((k2)x))+sin((k2)x)=2cos(x)sin((k1)x)+sin((k2)x)
终于,我们得到了一个不错的式子,通过这个式子可以发现k[m]>1时对f[n][m]的贡献为2cos(x)*f[n-1][m]+f[n-2][m]
然后就可以矩阵乘法了。
复杂度O(8*m^3log n)

代码

#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
const int maxn=60+5;
int i,j,n,m,t,k;
struct ar{
    double a[maxn][maxn];
} a,c;
double x;
ar ch(ar a,ar b){
    ar c;
    memset(c.a,0,sizeof(c.a));
    fo(i,1,m*2)
    fo(j,1,m*2)
    fo(k,1,m*2) c.a[i][j]=c.a[i][j]+a.a[i][k]*b.a[k][j];
    return c;
}
int main(){
    scanf("%d",&t);
    while (t){
        t--;
        scanf("%d%d%lf",&m,&n,&x);
        memset(a.a,0,sizeof(a.a));
        memset(c.a,0,sizeof(c.a));
        fo(i,m+1,m*2){
            a.a[i][i-m]=1,a.a[i-m][i]=-1;
            a.a[i][i]=2*cos(x);if (i<m*2)a.a[i][i+1]=sin(x);
        }
        c.a[1][1]=sin(x);c.a[1][m+1]=sin(2*x);c.a[1][m+2]=sin(x)*sin(x);
        n--;
        while (n){
            if (n%2)c=ch(c,a);
            a=ch(a,a);
            n/=2;
        }
        double s=c.a[1][m];
        if (s>0) printf("+");else printf("-");
        s=fabs(s);
        if (s<1){
            double s1=0.1;
            while (1) if (s>s1) {printf("%d\n",(int)(s/s1));break;}else s1*=0.1;
        }else{
            while (s)if (s<10) {printf("%d\n",(int)s);break;}else s/=10;

        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值