Overview
【題意】給出標號爲
【範圍】
Analysis
上面的題解的推導已經很詳細了,公式如下:
先把特殊情況和非法情況處理掉。
對於
對於
接着計算公式。
由於所有看得見的數值都是可直接計算的,所以考慮記錄因數個數,然後再用高精度乘單精度。
先明確一點,由於我們最後的結果是整數,所以不論公式長什麼樣,一定有因數個數非負。
對於階乘,可以預處理出每個數的情況,然後用前綴和;也可以不斷整除取餘來累加(小學希望杯培訓……)
對於
Sumarize
高精度中分解質因數,將因數累加的使用:
①已知的數都可以直接計算;
②階乘可以直接用整除來分解,也可以使用前綴和;
③對於小的數,最好用歐拉篩法等方法先求出所有的質因數來優化時間。
Prufer編碼:
①構造與還原的方法;
②經常跟度數和樹的計數有關。
Code
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
const int N=1010;
int n,d[N];
int cnt[N],sum;
int v[N],p[N],c[N][N];
int rc[N],res[N];
inline int read(void)
{
int s=0,f=1; char c=getchar();
for (;c<'0'||c>'9';c=getchar()) if (c=='-') f=-1;
for (;'0'<=c&&c<='9';c=getchar()) s=(s<<1)+(s<<3)+c-'0';
return s*f;
}
int mutiply(int k)
{
int m,g=0;
for (int i=1;i<=res[0];i++)
{
m=res[i]*k+g;
res[i]=m%10;
g=m/10;
}
for (;g;g/=10) res[++res[0]]=g%10;
}
int main(void)
{
n=read();
for (int i=1;i<=n;i++) d[i]=read();
for (int i=1;i<=n;i++)
{
if (!d[i]) {printf("0\n");return 0;}
if (d[i]>0) sum+=cnt[++cnt[0]]=d[i]-1;
}
if (n==1) {printf("%d\n",!cnt[0]?1:0);return 0;}
if (n-2<sum) {printf("0\n");return 0;}
for (int i=2;i<=n;i++)
{
if (!v[i]) p[++p[0]]=i;
for (int j=1;j<=p[0];j++)
{
if (i*p[j]>n) break;
v[i*p[j]]=1;
if (i%p[j]==0) break;
}
}
for (int i=1;i<=n;i++)
for (int j=1;j<=p[0];j++)
for (int k=i;k!=1&&k%p[j]==0;k/=p[j]) c[i][j]++;
for (int i=1;i<=p[0];i++) rc[i]+=c[n-cnt[0]][i]*(n-2-sum);
for (int i=1;i<=n;i++)
for (int j=1;j<=p[0];j++) c[i][j]+=c[i-1][j];
for (int i=1;i<=p[0];i++) rc[i]+=c[n-2][i];
for (int i=1;i<=p[0];i++) rc[i]-=c[n-2-sum][i];
for (int i=1;i<=cnt[0];i++)
for (int j=1;j<=p[0];j++) rc[j]-=c[cnt[i]][j];
res[0]=res[1]=1;
for (int i=1;i<=p[0];i++)
for (int j=1;j<=rc[i];j++) mutiply(p[i]);
for (int i=res[0];i;i--) printf("%d",res[i]);
printf("\n");
return 0;
}