Just Sum It [UvaLive 5063] DP+組合數

來源:互聯網
上載者:User

TLE到死,加了n多最佳化才過,淚流滿面.

題意:

  給你a1,a2..a9,ai表示有ai個i,問用這些數字組成的不同的數的和MOD 1000000007以後是多少.

題解:

  首先方法顯然:

       枚舉位元,枚舉某個數在哪個位上,假設有l位,數i在第j位上,則i在這個位上貢獻的值為i*10^(j-1)*(剩下的數排列成l-1位的不同排列個數).

       在所有不同的位元下,所有數在不同位置上貢獻的值求和即是所求結果.

然後痛點是如何求:剩下的數排列成l-1位的不同排列個數

   暴力:枚舉每個數用了幾個,如sigma(pi)==l-1,則結果為(l-1)!/((p1!)*(p2!)*..(pi!)),最壞10^10次,必然TLE

   DP:

        dp[i][j]表示用了1..i-1這些數構成j位的排列數.

        狀態方程:

             dp[i+1][j+k]+=dp[i][j]*C(j+k,k) (0=<k<=ai)

改成DP後本以為能A了,卻還是TLE.

最佳化1:

仔細觀察下可以發現,在位元確定的情況下,每個數不管在哪個位上,(剩下的數排列成l-1位的不同排列個數)不變,比方說6XX,X6X,XX6,這兩個XX排列個數是確定的,所以可以直接666*(XX的排列個數)

最佳化2:

在加了最佳化1後還是T,發現算上面的666還可以最佳化,666=6*111,可以先預先處理出不同長度的1..1的值,到時候直接乘之

最佳化3:

仍然T,改變一下枚舉順序,先枚舉數字,再枚舉長度,結果完全等價,但是可以先把改數字用過一次後剩下的數的排列的dp值算出來了,而不用再在每位的時候算一次了,最後複雜度為O(9*9*9)

估計這題資料群組數較多,時間卡的也太緊了.

代碼:

#include <cstdlib>

#include <stdio.h>

#include <iostream>

#include <memory.h>

#include <stdio.h>

using namespace std;

#define MOD 1000000007

long long extended_gcd(long long a,long long b,long long &k,long long &t)

{

  if (b==0)

  {

   k=1;

   t=0;

   return a;

 

  }

  else

  {

    long long tp_gcd;

    tp_gcd=extended_gcd(b,a%b,k,t);

    long long temp;

    temp=k;

    k=t;

    t=temp-(a/b)*t;

    return tp_gcd;

  }

}

long long in[1000];

long long inv(long long x)

{

    long long k,t;

    if (in[x]!=-1) return in[x];

    extended_gcd(x,MOD,k,t);

    while(k<0) k+=MOD;

    while(k>=MOD) k-=MOD;

    in[x]=k;

    return k;

}

int a[20];

long long dp[20][100];

long long Cv[200][200];

long long p11[200];

long long C(int n,int x)

{

    if (Cv[n][x]!=-1) {return Cv[n][x];}

    long long ans=1;

    int i,j;

    for(i=1;i<=n;i++)

        ans=ans*i%MOD;

    for(i=1;i<=x;i++)

        ans=ans*inv(i)%MOD;

    for(i=1;i<=n-x;i++)

        ans=ans*inv(i)%MOD;

    Cv[n][x]=ans;

    return ans;

}

long long cal_dp()

{

    memset(dp,0,sizeof(dp));

    int i,j;

    dp[1][0]=1;

    for(i=1;i<=9;i++)

        for(j=0;j<=90;j++)

        {

            long long tmp=dp[i][j];

            if (tmp==0) continue;

            for(int k=0;k<=a[i];k++)

                dp[i+1][j+k]=(dp[i+1][j+k]+Cv[j+k][j]*tmp%MOD)%MOD;

        }

}

int main(int argc, char** argv)

{

    long long tcase,i,j,sum,ans;

    memset(in,-1,sizeof(in));

    scanf("%lld",&tcase);

    for(i=0;i<=100;i++)

        for(j=0;j<=100;j++)

            Cv[i][j]=-1;

    for(i=0;i<=100;i++)

        for(j=0;j<=100;j++)

            C(i,j);

    p11[1]=1;

    for(i=2;i<=100;i++)

        p11[i]=(p11[i-1]*10+1)%MOD;

    while(tcase--)

    {

        sum=0;

        memset(a,0,sizeof(a));

        for(i=1;i<=9;i++)

        {

            scanf("%d",&a[i]);

           sum+=a[i];

        }

        ans=0;

        for(i=1;i<=9;i++) //第幾個

              if (a[i]>0) 

             {

                 a[i]--;

                 cal_dp();

               for(int l=1;l<=sum;l++) //位元

               {

                  long long t=0;

                  t=i*p11[l];  

                  long long ddd=dp[10][l-1];

                  ans=(ans+t*ddd%MOD)%MOD;

                //  if (ans<0) ans+=MOD; 

               }

                 a[i]++;

        }

       // cout<<ans<<endl;

        printf("%lld/n",ans);

    }

    return 0;

}

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.