2020 Multi-University Training Contest 3 1006 X Number
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6796
题意:有11中类别,分别为0到10类别,如果某一个数他数中出现最多次数的是d(0<=d<=9),那这个数是d类别,比如233中3出现了2次,是最多的,所以是3类别,如果最多出现的不止一个数字,那就是10类别,比如22334,现在给你一个区间[l,r]和类别d(0<=d<=9),问你这个区间中有多少个数是d类别的。
思路:首先肯定会想到数位dp,但会发现进行记忆化搜索时那个状态不好确定。这里就可以进行魔改,当你搜索到上一个数字已经不处于边界时,知道了d数字的个数已经其他数字的个数,就可以用组合数算出来。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[20],b[20],c[20][20];
ll x,l,r;
map<array<int,10>,ll>dp[20][2];
array<int,10>state= {0};
ll fun(int n,int m)
{
m=max(m,n-m);
if(c[n][m]!=-1)
return c[n][m];
ll ans=1;
for(int i=m+1; i<=n; i++)
ans*=i;
for(int i=1; i<=n-m; i++)
ans/=i;
return c[n][m]=ans;
}
ll dfs(int len,bool maxi,bool f)
{
if(dp[len][f].find(state)!=dp[len][f].end()&&maxi==0)
return dp[len][f][state];
if(!len)
{
for(int i=0; i<10; i++)
if(i!=x&&state[i]>=state[x])
return dp[len][f][state]=0;
return dp[len][f][state]=1;
}
ll res=0,maxn=maxi?a[len]:9;
if(f&&!maxi)
{
for(int i=0; i<=len; i++)
{
for(int i0=0; i0<=len-i; i0++)
b[i0]=0;
b[len-i]=fun(len,i);
for(int i0=0; i0<10; i0++)
{
if(i0==x)
continue;
if(state[x]+i<=state[i0])
{
b[0]=0;
break;
}
for(int i1=0; i1<=len-i; i1++)
{
for(int i2=1; i2<=min(i1,state[x]+i-state[i0]-1); i2++)
{
b[i1-i2]+=fun(i1,i2)*b[i1];
}
}
}
res+=b[0];
}
return dp[len][f][state]=res;
}
for(int i=0; i<=maxn; i++)
{
if(i||f)
{
if(i==x||state[i]+2<state[x]+len)
{
state[i]++;
res+=dfs(len-1,maxi&&i==a[len],1);
state[i]--;
}
}
else
res+=dfs(len-1,maxi&&i==a[len],0);
}
return maxi?res:dp[len][f][state]=res;
}
ll div(ll tmp)
{
int p=0;
while(tmp)
a[++p]=tmp%10,tmp/=10;
ll res=0;
res+=dfs(p,1,0);
return res;
}
int main()
{
memset(c,-1,sizeof(c));
int T;
cin>>T;
while(T--)
{
for(int i=0; i<20; i++)
for(int i1=0; i1<2; i1++)
dp[i][i1].clear();
cin>>l>>r>>x;
cout<<div(r)-div(l-1)<<endl;
}
}
浙公网安备 33010602011771号