1. 程式人生 > >2016年ACM/ICPC北京賽區 I題(思維 二項式展開)

2016年ACM/ICPC北京賽區 I題(思維 二項式展開)

題目連結:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=5698

題意:給你一個只由0~9字元構成的長度為n(n<=50000)的字串a,

定義,求對於每個i(i=1,2,...,n) ,輸出。答案對1e9+7取模。

由題意,存在表示式


令s[i]為a[i]的字首和,再進行二項式展開,則有

因此

組合數可以O(k^2)預處理,s[i]可以O(n)預處理,s[i]^k可以O(n*k)預處理,s[i]^k的字首和也可以O(n*k)預處理,對於每個i,O(k)計算ans[i],總複雜度為O(n*k)

程式碼:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mo=1e9+7;
const int maxn=50010;
ll s[maxn],ss[maxn][101],sss[maxn][101];
ll c[101][101],n,k;
char a[maxn];
void init()
{
    for(int i=0;i<=100;i++)
    {
        c[i][0]=1;
        for(int j=1;j<=i;j++)
        {
            c[i][j]=(c[i-1][j]+c[i-1][j-1])%mo;
        }
    }
}
int main()
{
    init();
    int T,cas=1;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%lld%lld",&n,&k);
        getchar();
        scanf("%s",a+1);
        s[0]=0;
        for(ll i=1;i<=n;i++)
        {
            s[i]=s[i-1]+(a[i]&15);
        }
        for(ll i=1;i<=n;i++)
        {
            ss[i][0]=1;
            sss[i][0]=0;
            for(ll j=1;j<=k;j++)
            {
                ss[i][j]=(ss[i][j-1]*s[i])%mo;
                sss[i][j]=(sss[i-1][j]+ss[i][j])%mo;
            }
        }
        for(ll i=1;i<=n;i++)
        {
            ll ans=i*c[k][0]%mo*ss[i][k]%mo;
            ll tmp;
            for(ll j=1;j<=k;j++)
            {
                if(j&1) tmp=-sss[i-1][j];
                else tmp=sss[i-1][j];
                ans=(ans+(c[k][j]*ss[i][k-j]%mo*tmp)%mo)%mo;
            }
            ans=(ans+mo)%mo;
            printf("%lld%c",ans,i==n?'\n':' ');
        }
    }
    return 0;
}