【洛谷5月月赛】玩游戏(NTT,生成函数)

本文解析了一道洛谷编程比赛题目,通过使用生成函数和NTT快速傅里叶变换来解决一个关于多项式计算的问题。文章详细介绍了如何利用二项式定理、生成函数的对数变换等数学工具简化复杂计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【洛谷5月月赛】玩游戏(NTT,生成函数)

题面

Luogu

题解

看一下要求的是什么东西
\((a_x+b_y)^i\)的期望。期望显然是所有答案和的平均数。
所以求出所有的答案就在乘一个逆元就好了。
现在考虑怎么算上面那个东西。
对于单个的计算,我们可以用二项式定理直接展开
得到
\[\begin{aligned}\sum(a+b)^k&=\sum\sum_{i=0}^kC_k^ia^ib^{k-i}\\&=\sum_{i=0}^kC_k^i(\sum a^i)(\sum b^{k-i})\\&=\sum_{i=0}^k\frac{k!}{i!(k-i)!}(\sum a^i)(\sum b^{k-i})\\&=k!\sum_{i=0}^k\frac{\sum a^i}{i!}\frac{\sum b^{k-i}}{(k-i)!} \end{aligned}\]
这样就是很明显的卷积的形式了。
现在考虑怎么计算\(\sum a^i\)
构造\(G(x)=\prod_{i=1}^n(1+a_ix)\)
然后对于\(G(x)\)\(ln\),再给第\(i\)项乘上\(i\)就好了。
为什么?
因为是乘积的形式,所以\(ln\)之后等价于对于所有东西都先\(ln\)在求和
所以考虑一下单个的\(ln\)值是什么,然后有\(ln'(A(x))=\frac{A'(x)}{A(x)}\)
这个手动用生成函数玩一下就知道了为啥了。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MOD 998244353
#define MAX 888888
inline int read()
{
    RG int x=0,t=1;RG char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
int fpow(int a,int b)
{
    int s=1;
    while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}
    return s;
}
int r[MAX],W[MAX];
void NTT(int *P,int len,int opt)
{
    int N,l=0;
    for(N=1;N<len;N<<=1)++l;
    for(int i=0;i<N;++i)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
    for(int i=0;i<N;++i)if(i<r[i])swap(P[i],P[r[i]]);
    for(int i=1;i<N;i<<=1)
    {
        int w=fpow(3,(MOD-1)/(i<<1));W[0]=1;
        for(int k=1;k<i;++k)W[k]=1ll*W[k-1]*w%MOD;
        for(int p=i<<1,j=0;j<N;j+=p)
            for(int k=0;k<i;++k)
            {
                int X=P[j+k],Y=1ll*P[i+j+k]*W[k]%MOD;
                P[j+k]=(X+Y)%MOD;P[i+j+k]=(X+MOD-Y)%MOD;
            }
    }
    if(opt==-1)
    {
        reverse(&P[1],&P[N]);
        for(int i=0,inv=fpow(N,MOD-2);i<N;++i)P[i]=1ll*P[i]*inv%MOD;
    }
}
int n,m,t[MAX];
int S1[MAX],S2[MAX];
int inv[MAX];
int F[MAX],P[MAX];
int jc[MAX],jcinv[MAX];
int tmp[50][MAX],St[50],top;
void Solve(int l,int r,int *P,int *t)
{
    if(l==r){P[1]=t[l];P[0]=1;return;}
    int mid=(l+r)>>1,N,ls=St[top--];
    Solve(l,mid,tmp[ls],t);
    int rs=St[top--];
    Solve(mid+1,r,tmp[rs],t);
    for(N=1;N<=r-l+1;N<<=1);
    NTT(tmp[ls],N,1);NTT(tmp[rs],N,1);
    for(int i=0;i<N;++i)P[i]=1ll*tmp[ls][i]*tmp[rs][i]%MOD;
    NTT(P,N,-1);
    St[++top]=ls;St[++top]=rs;
    for(int i=0;i<N;++i)tmp[ls][i]=tmp[rs][i]=0;
}
namespace Poly
{
    int A[MAX],B[MAX];
    void Inv(int *a,int *b,int len)
    {
        if(len==1){b[0]=fpow(a[0],MOD-2);return;}
        Inv(a,b,len>>1);
        for(int i=0;i<len;++i)A[i]=a[i],B[i]=b[i];
        NTT(A,len<<1,1);NTT(B,len<<1,1);
        for(int i=0;i<len<<1;++i)A[i]=1ll*A[i]*B[i]%MOD*B[i]%MOD;
        NTT(A,len<<1,-1);
        for(int i=0;i<len;++i)b[i]=(b[i]+b[i])%MOD;
        for(int i=0;i<len;++i)b[i]=(b[i]+MOD-A[i])%MOD;
        for(int i=0;i<len<<1;++i)A[i]=B[i]=0;
    }
    void Dao(int *a,int *b,int len)
    {
        for(int i=1;i<len;++i)b[i-1]=1ll*a[i]*i%MOD;
        b[len]=b[len-1]=0;
    }
    void Jifen(int *a,int *b,int len)
    {
        for(int i=1;i<len;++i)b[i]=1ll*a[i-1]*inv[i]%MOD;
        b[0]=0;
    }
    int C[MAX],D[MAX];
    void ln(int *a,int *b,int len)
    {
        memset(C,0,sizeof(C));memset(D,0,sizeof(D));
        Dao(a,C,len);Inv(a,D,len);
        NTT(C,len<<1,1);NTT(D,len<<1,1);
        for(int i=0;i<len<<1;++i)C[i]=1ll*C[i]*D[i]%MOD;
        NTT(C,len<<1,-1);Jifen(C,b,len);
    }
    int E[MAX],G[MAX];
    void Exp(int *a,int *b,int len)
    {
        if(len==1){b[0]=1;return;}
        Exp(a,b,len>>1);ln(b,E,len);
        for(int i=0;i<len;++i)E[i]=(MOD-E[i]+a[i])%MOD;E[0]=(E[0]+1)%MOD;       
        for(int i=0;i<len;++i)G[i]=b[i];
        NTT(E,len<<1,1);NTT(G,len<<1,1);
        for(int i=0;i<len<<1;++i)E[i]=1ll*E[i]*G[i]%MOD;
        NTT(E,len<<1,-1);
        for(int i=0;i<len;++i)b[i]=E[i];
        for(int i=0;i<len<<1;++i)E[i]=G[i]=0;
    }
}
int A[MAX],B[MAX],K;
int SA[MAX],SB[MAX];
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;++i)A[i]=read();
    for(int i=1;i<=m;++i)B[i]=read();
    K=read();
    inv[0]=inv[1]=1;
    int len;for(len=1;len<=max(n,m)+K;len<<=1);
    for(int i=2;i<(len<<1);++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
    for(int i=0;i<50;++i)St[++top]=i;Solve(1,n,S1,A);Poly::ln(S1,SA,len);top=0;
    for(int i=0;i<50;++i)St[++top]=i;Solve(1,m,S2,B);Poly::ln(S2,SB,len);top=0;
    for(int i=0;i<len;++i)SA[i]=1ll*SA[i]*i%MOD;
    for(int i=0;i<len;i+=2)SA[i]=(MOD-SA[i])%MOD;
    for(int i=0;i<len;++i)SB[i]=1ll*SB[i]*i%MOD;
    for(int i=0;i<len;i+=2)SB[i]=(MOD-SB[i])%MOD;
    SA[0]=n;SB[0]=m;jc[0]=jcinv[0]=1;
    for(int i=1;i<len;++i)jc[i]=1ll*i*jc[i-1]%MOD;
    for(int i=1;i<len;++i)jcinv[i]=1ll*jcinv[i-1]*inv[i]%MOD;
    memset(A,0,sizeof(A));memset(B,0,sizeof(B));
    for(int i=0;i<=K;++i)A[i]=1ll*SA[i]*jcinv[i]%MOD;
    for(int i=0;i<=K;++i)B[i]=1ll*SB[i]*jcinv[i]%MOD;
    for(len=1;len<=K+K;len<<=1);
    NTT(A,len,1);NTT(B,len,1);
    for(int i=0;i<len;++i)A[i]=1ll*A[i]*B[i]%MOD;
    NTT(A,len,-1);
    for(int i=1,inv=fpow(1ll*n*m%MOD,MOD-2);i<=K;++i)
    {
        int ans=1ll*A[i]*jc[i]%MOD*inv%MOD;
        printf("%d\n",ans);
    }
    return 0;
}

转载于:https://www.cnblogs.com/cjyyb/p/9175938.html

基于数据挖掘的音乐推荐系统设计与实现 需要一个代码说明,不需要论文 采用python语言,django框架,mysql数据库开发 编程环境:pycharm,mysql8.0 系统分为前台+后台模式开发 网站前台: 用户注册, 登录 搜索音乐,音乐欣赏(可以在线进行播放) 用户登陆时选择相关感兴趣的音乐风格 音乐收藏 音乐推荐算法:(重点) 本课题需要大量用户行为(如播放记录、收藏列表)、音乐特征(如音频特征、歌曲元数据)等数据 (1)根据用户之间相似性或关联性,给一个用户推荐与其相似或有关联的其他用户所感兴趣的音乐; (2)根据音乐之间的相似性或关联性,给一个用户推荐与其感兴趣的音乐相似或有关联的其他音乐。 基于用户的推荐和基于物品的推荐 其中基于用户的推荐是基于用户的相似度找出相似相似用户,然后向目标用户推荐其相似用户喜欢的东西(和你类似的人也喜欢**东西); 而基于物品的推荐是基于物品的相似度找出相似的物品做推荐(喜欢该音乐的人还喜欢了**音乐); 管理员 管理员信息管理 注册用户管理,审核 音乐爬虫(爬虫方式爬取网站音乐数据) 音乐信息管理(上传歌曲MP3,以便前台播放) 音乐收藏管理 用户 用户资料修改 我的音乐收藏 完整前后端源码,部署后可正常运行! 环境说明 开发语言:python后端 python版本:3.7 数据库:mysql 5.7+ 数据库工具:Navicat11+ 开发软件:pycharm
MPU6050是一款广泛应用在无人机、机器人和运动设备中的六轴姿态传感器,它集成了三轴陀螺仪和三轴加速度计。这款传感器能够实时监测并提供设备的角速度和线性加速度数据,对于理解物体的动态运动状态至关重要。在Arduino平台上,通过特定的库文件可以方便地与MPU6050进行通信,获取并解析传感器数据。 `MPU6050.cpp`和`MPU6050.h`是Arduino库的关键组成部分。`MPU6050.h`是头文件,包含了定义传感器接口和函数声明。它定义了类`MPU6050`,该类包含了初始化传感器、读取数据等方法。例如,`begin()`函数用于设置传感器的工作模式和I2C地址,`getAcceleration()`和`getGyroscope()`则分别用于获取加速度和角速度数据。 在Arduino项目中,首先需要包含`MPU6050.h`头文件,然后创建`MPU6050`对象,并调用`begin()`函数初始化传感器。之后,可以通过循环调用`getAcceleration()`和`getGyroscope()`来不断更新传感器读数。为了处理这些原始数据,通常还需要进行校准和滤波,以消除噪声和漂移。 I2C通信协议是MPU6050与Arduino交互的基础,它是一种低引脚数的串行通信协议,允许多个设备共享一对数据线。Arduino板上的Wire库提供了I2C通信的底层支持,使得用户无需深入了解通信细节,就能方便地与MPU6050交互。 MPU6050传感器的数据包括加速度(X、Y、Z轴)和角速度(同样为X、Y、Z轴)。加速度数据可以用来计算物体的静态位置和动态运动,而角速度数据则能反映物体转动的速度。结合这两个数据,可以进一步计算出物体的姿态(如角度和角速度变化)。 在嵌入式开发领域,特别是使用STM32微控制器时,也可以找到类似的库来驱动MPU6050。STM32通常具有更强大的处理能力和更多的GPIO口,可以实现更复杂的控制算法。然而,基本的传感器操作流程和数据处理原理与Arduino平台相似。 在实际应用中,除了基本的传感器读取,还可能涉及到温度补偿、低功耗模式设置、DMP(数字运动处理器)功能的利用等高级特性。DMP可以帮助处理传感器数据,实现更高级的运动估计,减轻主控制器的计算负担。 MPU6050是一个强大的六轴传感器,广泛应用于各种需要实时运动追踪的项目中。通过 Arduino 或 STM32 的库文件,开发者可以轻松地与传感器交互,获取并处理数据,实现各种创新应用。博客和其他开源资源是学习和解决问题的重要途径,通过这些资源,开发者可以获得关于MPU6050的详细信息和实践指南
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值