HDU 5085 Counting problem

来源:互联网 发布:北大发展研究院知乎 编辑:程序博客网 时间:2024/05/16 10:54
自己平时不大喜欢写题解啥的(太懒了……),但是难得发现一道网上没有题解的题啊哈哈哈哈,那我还是写下好了。

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=5085
题意:↑↑↑↑↑↑↑↑↑点上面自己看吧↑↑↑↑↑↑↑↑↑↑↑

思路:看完了题目,是不是虽然可能没啥思路,但还是有点想法?提示一下,这题是被放在杭电集训队分块专题里面的一道题,所以这题应该莫队可解。
恩,反正我是不会用莫队解(表打我……),自己做这题的时候看了下BC官方的题解,官方题解说的是用f(x)表示[1,x]这个区间段里的答案,因此答案就是f(b)-f(a-1)了,题解里对这个函数是前面暴力枚举,后面用hash来储存和查找,然而我是渣渣,hash只会水题,因此自己只能看一下其他菊苣的代码了。
Bestcoder#15 ranklist
这里面有一位菊苣就是用的莫队,但是他的代码用了太多宏命令,自己读不动……所以接下来讲一个自己从排名第一的菊苣那里看到的数位dp的解法吧(我到底在扯什么……)

-----------------------分割线--------------------------

此题数位dp可解。
思路:对于每个f(x),用dfs枚举在其范围内符合要求的数,并记录,于是就得到答案。
关于时间复杂度,很容易看出没有剪枝的时间复杂度是O(10!*100)的,非常大,但是因为有了剪枝,时间复杂度变得不好估计了,这点很抱歉,我还是很弱,不会估计

具体操作见代码吧
自己说的可能不是特别清楚,见谅
#include <cstdio>typedef long long LL;LL a,b,s;int k,N;LL pow[20][20],argmt[20];int pos[20];//len:目前有几位数还没有被确定下来//sum:要组合成的数//top:目前在讨论的数字,先讨论9,再讨论8,直到0LL dfs(int len,LL sum,int top){if(len*pow[top][k]<sum) return 0;if(sum<0) return 0;if(top==0){if(sum) return 0;pos[0]=len;LL ret=argmt[N];for(int i=0;i<=9;++i) ret/=argmt[pos[i]];//一个全排列就是符合要求的数的个数,因为首位是什么已经在solve()中的for里面决定好了//所以后面首位是0也没关系,只要找到符合要求的数的个数,计算出它们可以组合出几种不同的数就好了return ret;}LL ret=0;for(int i=0;i<=len;++i){pos[top]=i;ret+=dfs(len-i,sum-i*pow[top][k],top-1);}return ret;}//在dfs前先做一些处理,比如剪枝,以及记录dfs开始前有几位数还没有被确定下来inline LL sub_solve(int len,LL sum){if(len==0) return sum==0;N=len;return dfs(len,sum,9);}int bit[20],len;//solve(LL x):用来求[1,x]内符合范围数字的个数LL solve(LL x){len=0;while(x) bit[++len]=x%10,x/=10;LL ret=0,sum=s;for(int i=len;i;--i){for(int j=0;j<bit[i];++j)ret+=sub_solve(i-1,sum-pow[j][k]);//目前讨论的数没有取到最大值(j没有等于bit[i]),则后面的数可以随便取sum-=pow[bit[i]][k];//这里表示sum取到了最大的值}ret+=(sum==0);return ret;}int main(){//先计算好排列数以及1到9内每个数的各种次方,后面会用到argmt[0]=1;for(int i=1;i<20;++i) argmt[i]=i*argmt[i-1];for(int i=1;i<10;++i){pow[i][0]=1;for(int j=1;j<16;++j) pow[i][j]=pow[i][j-1]*i;}while(~scanf("%I64d%I64d%d%I64d",&a,&b,&k,&s)){printf("%I64d\n",solve(b)-solve(a-1));}}


0 0