Description
有M个球,一开始每个球均有一个初始标号,标号范围为1~N且为整数,标号为i的球有ai个,并保证Σai = M。
每次操作等概率取出一个球(即取出每个球的概率均为1/M),若这个球标号为k(k < N),则将它重新标号为k +
1;若这个球标号为N,则将其重标号为1。(取出球后并不将其丢弃) 现在你需要求出,经过K次这样的操作后,每个标号的球的期望个数。
Input
第1行包含三个正整数N,M,K,表示了标号与球的个数以及操作次数。 第2行包含N个非负整数ai,表示初始标号为i的球有ai个。
Output
应包含N行,第i行为标号为i的球的期望个数,四舍五入保留3位小数。
Sample Input
2 3 2
3 0
Sample Output
1.667
1.333
HINT
【样例说明】
第1次操作后,由于标号为2球个数为0,所以必然是一个标号为1的球变为标号为2的球。所以有2个标号为1的球,有1个标号为2的球。
第2次操作后,有1/3的概率标号为2的球变为标号为1的球(此时标号为1的球有3个),有2/3的概率标号为1的球变为标号为2的球(此时标号为1的球有1个),所以标号为1的球的期望个数为1/33+2/31
= 5/3。同理可求出标号为2的球期望个数为4/3。【数据规模与约定】
对于10%的数据,N ≤ 5, M ≤ 5, K ≤ 10;
对于20%的数据,N ≤ 20, M ≤ 50, K ≤ 20;
对于30%的数据,N ≤ 100, M ≤ 100, K ≤ 100;
对于40%的数据,M ≤ 1000, K ≤ 1000;
对于100%的数据,N ≤ 1000, M ≤ 100,000,000, K ≤ 2,147,483,647。
题解
并不知道这个东西
首先写个暴力就可以验证dp方程是
f [ i ] [ j ] = f [ i − 1 ] [ j ] ∗ m − 1 m + f [ i − 1 ] [ p r e ] ∗ 1 m f[i][j]=f[i-1][j]*\frac{m-1}{m}+f[i-1][pre]*\frac{1}{m} f[i][j]=f[i−1][j]∗mm−1+f[i−1][pre]∗m1
这个显然可以写成一个矩乘的形式…
然后他是 n 3 l o g K n^3logK n3logK的…
并不知道矩阵还有另一种方法优化到 n 2 n^2 n2,所以跪了
上图是转移矩阵,他一个循环矩阵
注意到下面一行都是由上一行向右移一格得到的…
那么有一个性质,循环矩阵的次幂同样是一个循环矩阵
所以我们只需要保存第一行的状态是什么,朴素矩乘是
f [ i ] [ k ] = ∑ a [ i ] [ j ] ∗ b [ j ] [ k ] f[i][k]=\sum a[i][j]*b[j][k] f[i][k]=∑a[i][j]∗b[j][k]
这里矩乘的形式推一下就可以变为
f [ k ] = ∑ a [ k ] ∗ b [ k − j + 1 ] f[k]=\sum a[k]*b[k-j+1] f[k]=∑a[k]∗b[k−j+1]
其中 k − j + 1 k-j+1 k−j+1在 [ 1 , n ] [1,n] [1,n]中,如果小于1了就加个 n n n
那么快速幂就可以变为 n 2 l o g k n^2logk n2logk的,出来再做个朴素矩乘就可以了
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#include<set>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define pll pair<long long,long long>
#define pii pair<int,int>
using namespace std;
inline int read()
{
int f=1,x=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int stack[20];
inline void write(LL x)
{
if(x<0){putchar('-');x=-x;}
if(!x){putchar('0');return;}
int top=0;
while(x)stack[++top]=x%10,x/=10;
while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x);putchar(' ');}
inline void pr2(LL x){write(x);putchar('\n');}
const int MAXN=1005;
int n,m,K;
struct ma1
{
double m[MAXN];
ma1(){memset(m,0,sizeof(m));}
}st,tp1;
struct ma2
{
double m[MAXN][MAXN];
ma2(){memset(m,0,sizeof(m));}
}tp2;
ma1 mul1(ma1 u1,ma1 u2)
{
ma1 ret;
for(int k=1;k<=n;k++)
for(int j=1;j<=n;j++)
{
int lst=k-j+1;if(lst<1)lst+=n;
ret.m[k]+=u1.m[j]*u2.m[lst];
}
return ret;
}
ma1 mul2(ma2 u2,ma1 u1)
{
ma1 ret;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
ret.m[i]+=u2.m[i][j]*u1.m[j];
return ret;
}
ma1 pow_mod(ma1 u1,int b)
{
ma1 ret=u1;b--;
while(b)
{
if(b&1)ret=mul1(ret,u1);
u1=mul1(u1,u1);b>>=1;
}
return ret;
}
int main()
{
// freopen("2.in","r",stdin);
n=read();m=read();K=read();
for(int i=1;i<=n;i++)st.m[i]=read();
tp1.m[1]=(double)(m-1)/m;tp1.m[n]=(double)1/m;
tp1=pow_mod(tp1,K);
// printf("CHECKER\n");
for(int i=1;i<=n;i++)
{
int st=i;
for(int j=1;j<=n;j++)
{
tp2.m[i][st]=tp1.m[j];
// printf("%.3lf ",tp2.m[i][j]);
st++;if(st>n)st=1;
}
// puts("");
}
// puts("");
st=mul2(tp2,st);
for(int i=1;i<=n;i++)printf("%.3lf\n",st.m[i]);
return 0;
}