[NOIP模拟][数位DP]魔法数字
2017-06-09 20:30
357 查看
ASDFZ-NOIP2016模拟
题目
分析
题解分析:
设p = LCM(q1, q2, q3 … qn),则任意数 s = t * p + r (r < p),很容易推导出:s % qi = r % qi,也就是说只要我们知道一个数模 p 的余数,就能得到它模q1, q2, q3 … qn 的余数。 对于本题而言,p = LCM(1, 2, 3,4, 5, 6, 7, 8, 9) = 2520。我们先把[L, R]的问题转化为[0, R]的问题与[0, L - 1]问题的差值。而[0, x]的问题容易用数位DP解决,我们只需从高位到低位,状态记录当前在哪一位、前面的位数是否与 x相等、9 个数分别是否出现、模 p 的值即可。时间复杂度 O(10 * 2^9 * logR / log10 * 2520)。 (不了解该性质也可以直接应用最小表示法解决) 一个可行的优化:当我们知道尚未加入最后一位前的数模1、 2、3、4、6、7、9 的值时,是否为5 或8的倍数只和最后一位有关,如此可以把状态中的2520 压到252。
自己的分析:
这道题的暴力还是很好拿的。正解的话,首先那个所谓的可行优化貌似是必须的,因为不这样过不了所有点。正解的记忆化搜索让它的复杂度大大下降。而且个人认为8应该是后三位有关系,因为2520>1000,所以不影响。详解见代码注释。
附代码
题目
分析
题解分析:
设p = LCM(q1, q2, q3 … qn),则任意数 s = t * p + r (r < p),很容易推导出:s % qi = r % qi,也就是说只要我们知道一个数模 p 的余数,就能得到它模q1, q2, q3 … qn 的余数。 对于本题而言,p = LCM(1, 2, 3,4, 5, 6, 7, 8, 9) = 2520。我们先把[L, R]的问题转化为[0, R]的问题与[0, L - 1]问题的差值。而[0, x]的问题容易用数位DP解决,我们只需从高位到低位,状态记录当前在哪一位、前面的位数是否与 x相等、9 个数分别是否出现、模 p 的值即可。时间复杂度 O(10 * 2^9 * logR / log10 * 2520)。 (不了解该性质也可以直接应用最小表示法解决) 一个可行的优化:当我们知道尚未加入最后一位前的数模1、 2、3、4、6、7、9 的值时,是否为5 或8的倍数只和最后一位有关,如此可以把状态中的2520 压到252。
自己的分析:
这道题的暴力还是很好拿的。正解的话,首先那个所谓的可行优化貌似是必须的,因为不这样过不了所有点。正解的记忆化搜索让它的复杂度大大下降。而且个人认为8应该是后三位有关系,因为2520>1000,所以不影响。详解见代码注释。
附代码
#include<iostream> #include<cstring> #include<string> #include<cstdlib> #include<cstdio> #include<ctime> #include<queue> #include<iomanip> #include<set> #include<cctype> #include<ctime> #include<algorithm> using namespace std; int k; long long l,r,ans,bit[20],dp[20][512][252][2]; int len,mark[20]; inline< c118 /span> long long readlong() { char ch;long long i=0;int f=1; for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar()); if(ch=='-') { ch=getchar(); f=-1; } for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<3)+(i<<1)+ch-'0'; return i*f; } inline void solve(long long x)//计算出这个数有多少位,分别每位是什么 { memset(mark,0,sizeof(mark)); memset(dp,-1,sizeof(dp)); for(int i=1;i<20;i++) if(x<bit[i]) { len=i; break; } for(int i=len;i>=1;i--) { mark[i]=x%10; x=x/10; } } inline long long dfs(int pos,int vis,int mod,bool limit)//pos是此时的长度,mod就是当前数除以252的余数 { // limit判断是否达到了最高位 if(pos>len)//此时实际上是位数刚好相等,判断有无1~9数字,并且可以整除 { int cnt=0; if(vis&1) cnt++; if(vis&(1<<1)&&mod%2==0) cnt++; if(vis&(1<<2)&&mod%3==0) cnt++; if(vis&(1<<3)&&mod%4==0) cnt++; if(vis&(1<<4)&&mod%5==0) cnt++; if(vis&(1<<5)&&mod%6==0) cnt++; if(vis&(1<<6)&&mod%7==0) cnt++; if(vis&(1<<7)&&mod%8==0) cnt++; if(vis&(1<<8)&&mod%9==0) cnt++; if(cnt>=k) return 1; else return 0; } mod%=252; if(dp[pos][vis][mod][limit]!=-1)//记忆化搜索,如果以及搜过,则不用再搜。 return dp[pos][vis][mod][limit]; int end; if(limit)//只有达到最高位,才会受到原数每一位的限制 end=mark[pos]; else end=9; long long res=0; for(int i=0;i<=end;i++)//要枚举0,因为那些小的数,就是前面填充的0 { if(i!=0) res+=dfs(pos+1,vis|(1<<i-1),mod*10+i,limit&&(i==end));//vis相当于是以二进制存了当前数含有哪些数字 else //如果一个数是5或8的倍数,那么除最后一位外的前几位除以252的余数 res+=dfs(pos+1,vis,mod*10+i,limit&&(i==end)); //再乘以10加上最后一位,那么它也必然是5或8的倍数 } //乘10相当于模的是2520,而5和8的倍数与后三位有关 dp[pos][vis][mod][limit]=res; return dp[pos][vis][mod][limit]; } int main() { //freopen("magic.in","r",stdin); //freopen("magic.out","w",stdout); k=readlong();l=readlong();r=readlong(); bit[0]=1; for(int i=1;i<20;i++) bit[i]=bit[i-1]*10; solve(r); ans=dfs(1,0,0,true); solve(l-1); ans-=dfs(1,0,0,true); printf("%I64d",ans); return 0; }
相关文章推荐
- JZOJ5379. 【NOIP2017提高A组模拟9.21】Victor爱数字 数位DP
- 5332. 【NOIP2017提高A组模拟8.23】密码 AC自动机+数位DP
- 【JZOJ5330】【NOIP提高组模拟】密码(库默尔定理、数位DP)
- fzu 1896 神奇的魔法数字(数位dp)
- 【NOIP2013模拟联考12】数数(数位dp||类欧几里得)
- [NOIP模拟][数位DP]准考证号
- bzoj1833[ZJOI2010]count 数字计数 数位DP
- 51nod 1042 数字0-9的数量(数位dp)
- 20160930的考试,noip模拟】SPFA,LIS,状压dp
- 1042 数字0-9的数量(数位DP)
- 【ZJOI2010】数字计数 数位dp
- POJ 3252 Round Numbers(数位dp,区间中二进制表示时0的个数大于等于1的个数的数字的个数)
- jzoj3782 【NOIP2014模拟8.17】组队 (特殊情况的2sat,dp)
- BZOJ 1833 [ZJOI 2010] 数字统计 (数位DP)
- xbz分组题B 吉利数字 数位dp入门
- BZOJ 1833 [ZJOI2010]count 数字计数(数位dp)
- 【51Nod】1042 - 数字0-9的数量(数位dp & 递归)
- 数位dp 洛谷P2602 [ZJOI2010]数字计数
- 数位dp 总结 51nod数字1的数量 数字0-9的数量
- 51nod 1009 数字1的数量(数位dp)