[HAOI2010]计数(组合数学)(数位DP)

原题题意也就是给的数的全排列小于原数的个数。

我们可以很容易的想到重复元素的排列个数的公式。

但是我们发现阶乘的话很快就会爆long long啊(如果您想写高精请便)

之后我就尝试质因数分解。。。。但是遗憾的是太蒻了,没有成功,还是爆了。20分惨惨。最后也没有找出来哪里错了。

最后蒟蒻只好参考题解了。。。。。

之后我们再一考虑,会发现其实我们不需要重复元素排列个数的公式,我们只需要按照组合数的思想做即可。

假设我们现在有m个位置可以摆放,我们先放0,个数就是\(C_m^{cnt[0]}\),之后就只剩\(m-cnt[0]\)个位置了,那么\(ans+=C_{m-cnt[0]}^{cnt[1]}\)。。以此类推。。。

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
long long CC[1001][1001];
long long C(long long n,long long m){
    if(CC[n][m]) return CC[n][m];
    if(m==1) return n;
    if(m==0||m==n) return 1;
    if(m>n) return 0;
    CC[n][m]=C(n-1,m)+C(n-1,m-1);
    return CC[n][m];
}
int a[10],v[100];
long long ans;
int n;
string c;
long long solve(){
    long long ans=1;
    int m=n;
    for(int i=0;i<=9;i++)
		if(a[i])
			ans*=C(m,a[i]),m-=a[i];
    return ans;
}
int main()
{
    cin>>c;
	for(int i=0;i<c.length();i++)
			v[++n]=c[i]-'0',a[v[n]]++;
    int nn=n;
    for(int i=1;i<=nn;i++)
	{
        n--;
        for(int j=0;j<v[i];j++)
        	if(a[j]) 
				a[j]--,ans+=solve(),a[j]++;
        a[v[i]]--;
    }
    printf("%lld",ans);
}

最后蒟蒻在这里贴上自己的20分代码。。求dalao找错。。。。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
using namespace std;
long long ans;
vector<int>v;
string n;
int dig[60],sum[10];

inline long long solve(int cnt)
{
    long long cur_ans=1;
    int a[60];
    memset(a,0,sizeof(a));
    for(int i=1;i<=cnt;i++)
    {
        int now=i;
        for(int j=2;j<=now;j++)
            while(now%j==0)
                now/=j,a[j]++;//printf("a[%d]=%d\n",j,a[j]);
    }
    for(int i=0;i<=9;i++)
    {
        if(sum[i]==0) continue;
        int now=sum[i];
        for(int j=2;j<=now;j++)
            while(now%j==0)
                now/=j,a[j]--;//printf("a[%d]=%d\n",j,a[j]);
    }
    for(int i=1;i<=50;i++)
        if(a[i]!=0)
            cur_ans*=(long long)pow(i,a[i]);
    return cur_ans;
}

int main()
{
    cin>>n;
    for(int i=0;i<n.length();i++)
        v.push_back(n[i]-'0');
    int cnt=v.size();
    for(int i=0;i<v.size();i++)dig[i+1]=v[i];
    //for(int i=1;i<=cnt;i++) cout<<dig[i];
    //cout<<endl;
    for(int i=1;i<=cnt;i++)
    {
        int done[10];
        memset(done,0,sizeof(done));
        for(int j=i+1;j<=cnt;j++)
        {
            memset(sum,0,sizeof(sum));
            if(done[dig[j]]==0&&dig[j]<dig[i])
            {
                done[dig[j]]=1;
                for(int k=i+1;k<=cnt;k++)
                    sum[dig[k]]++;
                sum[dig[i]]++;
                sum[dig[j]]--;
                ans+=(long long)solve(cnt-i);
                //printf("dig[%d]=%d dig[%d]=%d ans=%lld\n",i,dig[i],j,dig[j],ans);
            }
        }
    }
    printf("%lld\n",ans);
    return 0;
}
posted @ 2018-10-15 21:19  风浔凌  阅读(202)  评论(0编辑  收藏  举报