题意:
多组数据,每组求第n个包含‘666’的数(不能断开),如1:666,2:1666,14:6667。
题解:
AC自动机解法没去想,数位DP没学,这里有一种类似于数位DP,却又与数位DP不同,我称为数位树。
数位树:
将数n如线段树一样地拆分成多个小段,进行递归处理得出答案。
本题详(lue)解:
直接看每一位应该是什么数,然后n减去相应的数,使得在下一层转换为子问题“在开头有b个连续的6时,求第a个带‘666’的数”。就是如此简单,如此简单!!!!
代码来啦!
#include <cstdio> #include <cstring> #include <algorithm> #define N 20/*数的最大保证位数*/ using namespace std; long long n; long long f0[N],f1[N],f2[N],f3[N]; long long f10[N],f5[N],f6[4][N]; /* 注一:[N]处为位数 注二:这里说的数不去首位0,即0066算4位数 f0:首位不为6且不含‘666’的数的个数 f1:首位 为6且不含‘666’的数的个数(第二位不为‘6’) f2:首位 为6且不含‘666’的数的个数(第二位 为‘6’) f3:含‘666’的数的个数 f5:5开头的含‘666’的数的个数 f6:1:开头含‘6’的数的个数 2:开头含‘66’的数的个数 3:含‘666’的数的个数 f10:同f3 */ long long power(long long x,long long p) { long long ans=1; while(p>0) { if(p&1)ans*=x; x*=x; p>>=1; } return ans; } void dfs(long long nn,long long nm)/*位数,已连续的6的个数*/ { if(!nn)/*没有位了,结束!*/ { printf("\n"); return ; } long long i,j,k; if(nm>=3)/*剪枝(如果前面已经‘666’了,那么直接算数就好)*/ { long long tens=1; n--; for(i=1;tens<=n;i++,tens*=10); for(;i<=nn;i++)printf("0"); if(n)printf("%I64d",n); printf("\n"); return ; } if(f5[nn]>=n)/*此位为0~5*/ { printf("%I64d",(n-1)/f10[nn-1]); n=(n-1)%f10[nn-1]+1; dfs(nn-1,0); return ; } else if(f6[3-nm][nn]>=n)/*此位为6*/ { printf("6"); n-=f5[nn]; dfs(nn-1,nm+1); return ; } else/*此位为7~9*/ { n-=f6[3-nm][nn]; printf("%I64d",(n-1)/f10[nn-1]+7); n=(n-1)%f10[nn-1]+1; dfs(nn-1,0); return ; } } void init()/*预处理*/ { long long i; f0[0]=1; for(i=1;i<N;i++) { f0[i]=(f0[i-1]+f1[i-1]+f2[i-1])*9; f1[i]=f0[i-1]; f2[i]=f1[i-1]; f3[i]=f3[i-1]*10+f2[i-1]; } for(i=3;i<N;i++)/*i位数中含‘666’的数的个数*/ { f10[i]=f3[i]; f5[i]=6*f10[i-1]; f6[3][i]=f10[i]-3*f10[i-1]; f6[2][i-1]=f6[3][i]-f5[i]-3*f10[i-2]; f6[1][i-2]=f6[2][i-1]-f5[i-1]-3*f10[i-3]; } } int main() { long long i,g; scanf("%I64d",&g); init(); while(g--) { scanf("%I64d",&n); for(i=3;f10[i]<n;i++); dfs(i,0); } return 0; }
【POJ3208】传说中POJ最难的数位DP?(正解AC自动机,二解数位DP,吾异与之)
原文地址:http://blog.csdn.net/vmurder/article/details/39322917