「雅礼集训 2018-03-31」Max

一个长为 nn 的序列 AA,从 11 开始标号,一开始全为 00,现在小 C 想对它进行 mm 次操作。对第 ii 次操作,他会选定恰好一个二元组 (j,k)(j,k)1jn,0kc1\leq j\leq n,0\leq k\leq c)并令 Aj=Aj+kA_j=A_j+k,其中选中二元组 (j,k)(j,k) 的概率为 Pi,j,kP_{i,j,k}。小 C 本来是想问你区间最大值的历史版本和的历史最大值的期望的,但鉴于这是一道签到题,现在他只想知道 mm 次操作后整个序列最大值的期望,对 109+710^9+7 取模。

Constraints

n40n \leq 40m10m \leq 10c3c \leq 3

Solution

f(i,S,j)f(i,S,j) 表示 AiA_{i} 在经过集合 SS 的操作后,值为 jj 的概率。

dp(i,S,j)dp(i,S,j) 表示前 ii 个元素,用掉了集合 SS 的操作,最大值为 jj 的概率,可得:

dp(i,S1,j)×f(i+1,S2,k)=dp(i+1,S1+S2,max(j,k))dp(i,S_{1},j)\times f(i+1,S_{2},k)=dp(i+1,S_{1}+S_{2},max(j,k))

详见代码。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include<cstdio>
#include<algorithm>
#include<cstring>
#define LL long long
using namespace std;
const int N=41;
const int M=11;
const int T=1<<10;
const int mod=1e9+7;
int n,m,c,S,x,y,val,ans;
int p[N][M][4],f[T][M*3],dp[N][T][M*3];
int read()
{
int x=0,f=1;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();}
return x*f;
}
void Mod(int &x,LL val){x=(x+val)%mod;}
int main()
{
n=read();m=read();c=read();S=(1<<m)-1;
for(int j=0;j<m;j++)
for(int i=1;i<=n;i++)
for(int k=0;k<=c;k++)
p[i][j][k]=read();
dp[0][0][0]=1;
for(int i=1;i<=n;i++)
{
memset(f,0,sizeof(f));
f[0][0]=1;
for(int s=0;s<=S;s++)
{
x=__builtin_popcount(s);
for(int j=0;j<m;j++)
{
if((1<<j)&s)break;
for(int k=0;k<=c*x;k++)
for(int d=0;d<=c;d++)
Mod(f[s|(1<<j)][k+d],1ll*f[s][k]*p[i][j][d]);
}
}
for(int s=0;s<=S;s++)
{
x=__builtin_popcount(s);
for(int k=0;k<=c*x;k++)
{
val=dp[i-1][s][k];
if(!val)continue;
for(int j=s^S;;j=(j-1)&(s^S))
{
y=__builtin_popcount(j);
for(int d=0;d<=c*y;d++)
Mod(dp[i][s^j][max(k,d)],1ll*val*f[j][d]);
if(!j)break;
}
}
}
}
for(int i=0;i<=c*m;i++)
ans=(ans+1ll*i*dp[n][S][i])%mod;
printf("%d",ans);
return 0;
}