【bzoj2510】【期望dp】【循环矩阵快速幂】弱题

【描述】
有M个球,一开始每个球均有一个初始标号,标号范围为1~N且为整数,标号为i的球有ai个,并保证Σai = M。

每次操作等概率取出一个球(即取出每个球的概率均为1/M),若这个球标号为k(k < N),则将它重新标号为k + 1;若这个球标号为N,则将其重标号为1。(取出球后并不将其丢弃)

现在你需要求出,经过K次这样的操作后,每个标号的球的期望个数。

【输入】
第1行包含三个正整数N,M,K,表示了标号与球的个数以及操作次数。

第2行包含N个非负整数ai,表示初始标号为i的球有ai个

【输出】
应包含N行,第i行为标号为i的球的期望个数,四舍五入保留3位小数。

【样例输入】
2 3 2
3 0

【样例输出】
1.667
1.333
N ≤ 1000 , M ≤ 100 , 000 , 000 , K ≤ 2 , 147 , 483 , 647 N ≤ 1000, M ≤ 100,000,000, K ≤ 2,147,483,647 N1000,M100,000,000,K2,147,483,647

【思路】

转移十分显然,定义 f [ i ] [ k ] f[i][k] f[i][k]表示k次操作之后编号为i的球的期望个数:
f [ i ] [ k ] = ( 1 / m ) ∗ f [ i − 1 ] [ k − 1 ] + ( ( m − 1 ) / m ) ∗ f [ i ] [ k − 1 ] f[i][k]=(1/m)*f[i-1][k-1]+((m-1)/m)*f[i][k-1] f[i][k]=(1/m)f[i1][k1]+((m1)/m)f[i][k1]
于是我们就可以矩阵快速幂转移啦。好像有什么不对…N<=1000。此时如果你有相当玄学的卡常技巧,你就可以去写代码了,相信你能把常数卡成1/1000 。我们发现,转移矩阵是循环相同的。

循环矩阵例子:
[ 1 2 3 3 1 2 2 3 1 ] \begin{bmatrix} 1 &amp; 2&amp;3\\ 3&amp; 1&amp;2 \\2&amp;3&amp;1 \end{bmatrix} 132213321
循环矩阵指的是上面这种矩阵。维护这样的矩阵很简单。我们发现,这样的矩阵自乘若干次以后仍然是循环矩阵,这意味着我们只需要维护一行即可。此时矩阵乘法类似于多项式乘法: c [ ( i + j ) m o d n ] + = a [ i ] ∗ b [ j ] c[(i+j)modn]+=a[i]*b[j] c[(i+j)modn]+=a[i]b[j]。很好记对吧?所以我们就可以在 O ( n 2 l o g n ) O(n^2logn) O(n2logn)内解决问题,甚至可以使用fft优化。(不过由于fft自带大常数,所以效率差不多)。
代码:

#include<bits/stdc++.h>
#define re register
using namespace std;
const int N=1e3+5;
inline int red(){
    int data=0;bool w=0; char ch=0;
    ch=getchar();
    while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
    if(ch=='-') w=1,ch=getchar();
    while(ch>='0' && ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
    return w?-data:data;
}
int n,m,c,k;
struct mat{
	double a[N];
	friend inline mat operator*(const mat&a,const mat&b){
		mat c;memset(c.a,0,sizeof(c.a));
		for(int re i=0;i<n;i++)
			for(int re j=0;j<n;j++)
				c.a[(i+j)%n]+=a.a[i]*b.a[j];
		return c;
	}
}ans,A;
void ksm(){while(k){if(k&1)ans=ans*A;A=A*A,k>>=1;}}
int main(){
	n=red();m=red();k=red();
	for(int re i=0;i^n;++i)ans.a[i]=red();
	A.a[0]=1.0*(m-1)/m,A.a[1]=1.0/m;ksm();
	for(int re i=0;i^n;++i)printf("%.3f\n",ans.a[i]);
}
### 回答1: bzoj作为一个计算机竞赛的在线评测系统,不仅可以提供大量的题目供程序员练习和学习,还可以帮助程序员提升算法和编程能力。为了更好地利用bzoj进行题目的学习和刷题,制定一个bzoj做题计划是非常有必要的。 首先,我们需要合理安排时间,每天留出一定的时间来做bzoj的题目。可以根据自己的时间安排,每天挑选适量的题目进行解答。可以先从难度较低的题目开始,逐渐提高难度,这样既能巩固基础知识,又能挑战自己的思维能力。 其次,要有一个计划和目标。可以规划一个每周或每月的题目数量目标,以及每个阶段要学习和掌握的算法知识点。可以根据bzoj的题目分类,如动态规划、图论、贪心算法等,结合自己的实际情况,有针对性地选择题目进行学习。 此外,要充分利用bzoj提供的资源。bzoj网站上有很多高质量的题解和优秀的解题代码,可以参考和学习。还有相关的讨论区,可以与其他程序员交流和讨论,共同进步。 最后,要坚持并保持思考。做题不是单纯为了刷数量,更重要的是学会思考和总结。遇到难题时,要有耐心,多思考,多尝试不同的解法。即使不能一次性解出来,也要学会思考和分析解题过程,以及可能出现的错误和优化。 总之,bzoj做题计划的关键在于合理安排时间、制定目标、利用资源、坚持思考。通过有计划的刷题,可以提高算法和编程能力,并培养解决问题的思维习惯,在计算机竞赛中取得更好的成绩。 ### 回答2: bzoj做题计划是指在bzoj这个在线测评系统上制定一套学习和刷题的计划,并且将计划记录在excel表格中。该计划主要包括以下几个方面的内容。 首先是学习目标的设定。通过分析自己的水平和知识缺口,可以设定一个合理的目标,比如每天解决一定数量的题目或者提高特定的算法掌握程度。 其次是题目选择的策略。在excel表格中可以记录下自己选择的题目编号、题目类型和难度等信息。可以根据题目的类型和难度来安排每天的刷题计划,确保自己可以逐步提高技巧和解题能力。 然后是学习进度的记录和管理。将每天的完成情况记录在excel表格中,可以清晰地看到自己的学习进度和任务完成情况。可以使用图表等功能来对学习进度进行可视化展示,更好地管理自己的学习计划。 同时,可以在excel表格的备注栏中记录下每道题目的解题思路、关键点和需要复习的知识点等信息。这样可以方便自己回顾和总结,巩固所学的知识。 最后,可以将excel表格与其他相关资料进行整合,比如算法教材、题目解析和学习笔记等。这样可以形成一个完整的学习档案,方便自己进行系统的学习和复习。 总之,bzoj做题计划excel的制定和记录可以帮助我们更加有条理和高效地进行学习和刷题。通过合理安排学习目标和题目选择策略,记录学习进度和思路,并整合其他学习资料,我们可以提高自己的解题能力,并在bzoj上取得更好的成绩。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值