无脑推公式题
一开始推出来
O(nk2)
的超时了,后来把复杂度降低到
O(nk)
才跑过去
对于每个答案
ans[i]
,存在表达式
ans[i]=∑ij=1F[j][i]
令
s[i]
为前缀和
F[j][i]=(s[i]−s[j−1])k
二项式展开:
F[j][i]=C0ks[i]k+C1ks[i]k−1(−s[j−1])+...+Ckk(−s[j−1])k
把所有
Cpks[i]k−p
提出来,剩下的用前缀和维护就行了
#include <iostream>
#include <cstdio>
#define N 100050
#define K 105
#define mod 1000000007LL
using namespace std;
typedef long long LL;
int n,k;
LL C[K][K],a[N],ans[N],sum[N],S[N][K],T[N][K],r[N][K];
char s[N];
void inc(LL &x,LL y) { x = (x+y) % mod; }
void solve() {
scanf("%d%d",&n,&k);
for (int i=0;i<=k;i++) C[i][0] = C[i][i] = 1LL;
for (int i=1;i<=k;i++)
for (int j=1;j<=i;j++) C[i][j] = ( 0LL + C[i-1][j-1] + C[i-1][j] ) % mod;
scanf("%s",s+1);
for (int i=1;i<=n;i++) a[i] = s[i] - '0';
for (int i=1;i<=n;i++) sum[i] = (sum[i-1] + a[i]) % mod;
// S[i][j] --> sum[i] ^ j
for (int i=0;i<=n;i++) S[i][0] = 1LL;
for (int i=1;i<=n;i++)
for (int j=1;j<=k;j++) S[i][j] = S[i][j-1] * sum[i] % mod;
/*
// T[i][j] --> (-sum[i]) ^ j
for (int i=0;i<=n;i++) T[i][0] = 1LL;
for (int i=1;i<=n;i++)
for (int j=1;j<=k;j++) {
T[i][j] = T[i][j-1] * (-sum[i]) % mod;
if (T[i][j] < 0) T[i][j] += mod;
}
*/
// r[i][j] --> sigma(p=1~i) s[p][j]
for (int j=0;j<=k;j++) r[0][j] = S[0][j];
for (int i=1;i<=n;i++)
for (int j=0;j<=k;j++)
r[i][j] = ( r[i-1][j] + S[i][j] ) % mod;
for (int i=1;i<=n;i++) {
ans[i] = 0LL;
for (int p=0;p<=k;p++)
if (p%2 == 0)
inc( ans[i] , C[k][p] * S[i][k-p] % mod * r[i-1][p] % mod );
else
inc( ans[i] , -C[k][p] * S[i][k-p] % mod * r[i-1][p] % mod );
if (ans[i] < 0) ans[i] += mod;
}
for (int i=1;i<=n;i++) printf("%d%c",(int)ans[i],i==n?'\n':' ');
return ;
}
int main() {
int T = 0; scanf("%d",&T);
for (int _=1;_<=T;_++) solve();
return 0;
}