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