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;
}