随 (rand)(巧用快速幂)(期望dp)

本文探讨了在大规模数据集上进行模运算和求期望值的高效算法。通过使用二进制拆分和快速幂技巧,文章提出了一种计算n个正整数经过m次随机选择相乘后的模p结果的期望值的方法。该方法适用于质数模p,并利用了原根和欧拉定理等数学原理。

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

【题目描述】
给出n个正整数a1,a2…an和一个质数mod.一个变量x初始为1.进行m次操作.每次在n个数中随机选一个ai,然后x=x*ai%mod.问m次操作之后x的取值的期望.
答案一定可以表示成a/b的精确分数形式.a和b可能很大,所以只需要输出a*(b^(10^9+5))模10^9+7的结果.
【输入格式】
第一行三个整数n,m,mod.
接下来一行n个空格隔开的正整数a1,a2…an
【输出格式】
一行一个整数表示答案
【样例输入】
2
1 3 1 2
【样例输出】
3
【数据范围】

第1个测试点:mod=2
第2个测试点:n=1
第3,4,5个测试点:m<=1000,1<=mod<=300.
第6,7,8个测试点:1<=mod<=300
对于全部测试点: 1<=ai<mod,mod为质数1<=mod<=1000,
对于全部测试点:1<=n<=10^5,1<=m<=10^9
【孙金宁教你学数学】
质数P的原根g满足1<=rt<P,且rt的1次方,2次方…(P-1)次方在模P意义下可以取遍1到(P-1)的所有整数.
欧拉定理:对于质数P,1<=x<P的任意x的P-1次方在模P意义下都为1.
显然,原根的1次方,2次方…(P-2)次方在模P意义下都不为1,只有(P-1)次方在模P意义下为1.这也是一个数成为原根的充分必要条件.

法一。Mod = 1e9 +7;

f[i][j]:i个数a(a1,a2....am)相乘模mod得j的方案数=、=。

因为f[i+1][j*k%mod] += (f[i][j] * k的个数)%Mod。m那么大。。。数组开不下,一个一个枚举也超时!

同理可得f[2*i][j*k%mod] += (f[i][j] * f[i][k])%Mod。

考虑把m二进制拆分。也就是f[2*i][j*k%mod] = (f[i][j] * f[i][k])%Mod。//数组开不下?滚起来!!!也可以只要一维,因为2*i,只会由i影响,拿一个辅助数组,把所有i得到的2i,先存在fz中,再赋回去。

如果m的二进制的第i位为1的话,那么所有的f[i][j]肯定是取的m个数的一部分。f[i][j]和快速幂的更新方法一样=、=

f[2*i][j*k%mod] += (f[i][j] * f[i][k])%Mod。

int mpow(int a,int b)
{
	int  rt = 1;
	for( ;b;b >>= 1, a = 1ll * a * a % modd)
	{
		if(b&1) rt = 1ll * a * rt %modd;
	}
	return rt;
}

上面是求a^b的板子,我们现在要求b个不同的a相乘。

现在m相当于b, f数组就相当于a。//这里的f[j]表示选了2^i个不同的a相乘%mod得j的方案数。

g相当于rt。//这里的g[j]表示选了2^i+(2^k) + 2^z...个不同的a(m的二进制的第i位,并且第i位为1,2^i个数是m个数的一部分,k,z同理)相乘%mod得j的方案数.

其实g[i+k+z)][j*jj%mod] += (f[i][j] * g[k+z][jj])%Mod(选了2^i个不同的a相乘%mod得j的方案数 * 选了2^(k+z)个不同的a相乘%mod得jj的方案数  ==选了2^(i+k+z)个不同的a相乘%mod得(j * jj%mod)的方案数 )

i是一位一位的把m给二进制拆分分的的。

最后的g[j]就是m个不同的数相乘%mod得j的方案数;

期望等于每个最后方案数*每个最后取值的和/总方案数。//我尽力了。。请各位感性理解一波=。=、

#include<bits/stdc++.h>
using namespace std;
const int modd = 1e9 + 7;
int n,m,mod,g[1005],fz[1005],f[1005];
void read(int &x)
{
	x = 0;  int f = 0;  char c = getchar();
	while(c < '0' || c > '9')
	{
		if(c == '-') f = 1; 	c  = getchar();
	}
	while(c >= '0' && c <= '9')
	{
		x = x * 10 + c - '0'; c = getchar();
	}
	if(f) x = -x ;
}
int mpow(int a,int b)
{
	int  rt = 1;
	for( ;b;b >>= 1, a = 1ll * a * a % modd)
	{
		if(b&1) rt = 1ll * a * rt %modd;
	}
	return rt;
}
void hehe()
{
		for(int i = 1;  i < mod ;i++) fz[i] = 0;
		for(int i = 1; i < mod ;i++)
		 for(int j = 1; j < mod; j++)
		 fz[1ll * i * j % mod] = ( fz[1ll * i * j % mod] + 1ll * f[i] * f[j] % modd) % modd;
		for(int i = 1; i < mod ;i ++)
		f[i] = fz[i];

}
int main()
{
	freopen ( "rand10.in" , "r" , stdin ) ; 
	freopen ( "rand.out" , "w" , stdout ) ; 
	read(n);read(m);read(mod);
	for(int i = 1; i <= n; i++) {
		int x ;read(x);f[x]++;
	}
	g[1] = 1; int ha = m;
	for( ; ha;ha >>= 1,hehe())
	{
	   if(ha & 1)
		{
		for(int i = 1;  i < mod ;i++) fz[i] = 0;
		   for(int i = 1; i < mod ;i++)
		   for(int j = 1; j < mod; j++)
		   fz[1ll * i * j % mod] = ( fz[1ll * i * j % mod] + 1ll * g[i] * f[j] % modd) % modd;
		  for(int i = 1; i < mod ;i ++)
		   g[i] = fz[i];
		}
	}
	int ans = 0;
	for(int i = 1; i < mod; i++) ans = (ans + 1ll * i * g[i]) % modd;
    printf("%d", 1ll * ans * mpow(mpow(n,m),modd-2) % modd);
	return 0;
} 

法二:原根rt的1次方,2次方…(P-1)次方在模P意义下可以取遍1到(P-1)的所有整数。所以不论是多少个a1,a2,a3,a5....an相乘。

都可以用rt表示。

f[i][j] :2^i个数相乘最后模mod得rt^j次方的方案数。当幂的次数(j)大于mod-1(p - 1)时,就从头来.

f[i][(j+k)%(mod-1)]  += f[i-1][k] *f[i-1][j];

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值