首先要吐两行槽:看到集训队论文上有这道题,由于数位DP一律写成记忆化搜索形式的强迫症,就没去看论文上的几个函数是什么……;结果被这道题虐的脑细胞死光……,最后是用随机数据对拍AC程序然后发现BUG改掉后才过掉的,花了一整天时间……。回头看论文,发现差不多……。

大概题意:给出一个long long范围的数N构成区间[1,N]和K(K<N),然后给出数的排名规则(参见原题),看到第一问求K的排名,稍稍考虑后觉得……这还用做?,第二问求第K大的数是什么,脑补N久之后感叹……这也能做?!  好吧,第一问是挺经典的数位统计问题,较简单,第二问,比较困难,很锻炼逆向思维。

思路:第一问大体解题轮廓:如果求出了位数为i,数位和为j的个数,然后把K按区间划分……,差不多是这样。细节处理:K的各位和为SUM,比SUM小的都加上,等于SUM字典序小于K的也加上……于是把比K排名小的数拆成这两部分计算会比较简单,设dp[i][j]为i位小于j的个数(含前导零),dp2[i][j]为i位等于j的个数(不含前导零)……,(其实dp2[i][j]可以用dp[i][j+1]-dp[i][j]表示,为了思路清晰就分开存了……,而且记忆化搜索懒惰求dp值的话,应该会出错……),第一部分区间划分比较简单,只需要考虑别超过N的限制就OK了,所以记忆化搜索只需要设一个标志f1:划分上界不超过N。第二部分繁琐些……,而且需要考虑当前是否有前导零(不明白的仔细考虑一下),觉得至少需要三个标志:f1:前导零标志,f2:划分上界不超过K(字典序限制),f3:划分上界不超过N……。这样就差不多了……

第二问大体解题轮廓:根据排名算数是不能按数位处理的……,所以要倒过来想:排名为K的数的SUM是多少?字典序又是如何的?  排名为K的SUM用第一问的第一个函数就能求出,枚举SUM的值判断是否大于K,大于K就停下,由于dp[i]数组的值是单调的,所以可以二分处理……。设SUM=X,那么第K个数就是SUM=X的所有数中字典序排名为(K-数位和<sum)的……,设这个排名为DX。然后这就又可以利用第一问的第二个函数去按位夹逼……,其实枚举每一位的值时也有单调性,可以二分,不过只枚举0~9,就没这必要了吧……,万一二分再写错……。细节注意:把SUM分配给每一位,SUM减到零时并不一定就是所求,可能还有后导零……。

不知道怎么说的更清楚点了……,还是多动脑想细节吧……。

 #include <cstdio>
#include <cstring>
#include <algorithm> using namespace std;
typedef long long ll; ll dp[][];
ll dp2[][];
int d1[],d2[];
ll dfs(int w,int sum,bool f1)
{
if(sum<=)return ;
if(!w)return sum>;
if(!f1&&~dp[w][sum])return dp[w][sum];
int e=f1?d1[w]:;
ll ans=;
for(int i=;i<=e;++i)
ans+=dfs(w-,sum-i,f1&&(i==e));
if(!f1)dp[w][sum]=ans;
return ans;
} ll dfs2(int w1,int w2,int sum,bool f1,bool f2,bool f3)
{
if(sum<)return ;
if(!w1)return sum==;
if(!f2&&!f3&&~dp2[w1][sum])return dp2[w1][sum];
int e=f3?d1[w1]:;
ll ans=;
if(f2)e=min(e,d2[w2]);
for(int i=;i<=e;++i)
{
if(!i&&f1)ans+=dfs2(w1-,w2,sum,,,);
else ans+=dfs2(w1-,w2+,sum-i,,f2&&(i==d2[w2]),f3&&(i==d1[w1]));
}
if(!f2&&!f3)dp2[w1][sum]=ans;
return ans;
} ll cal(ll n,ll m)
{
int c1,c2,sum;
for(c1=;n;d1[++c1]=n%,n/=);
for(c2=,sum=;m;d2[++c2]=m%,sum+=d2[c2],m/=);
d2[c2+]=-;
for(int i=;i<c2/;++i)swap(d2[i+],d2[c2-i]);
return dfs(c1,sum,)-+dfs2(c1,,sum,,,);
}
ll cal2(ll n,ll k)
{
int c1,c2=;
for(c1=;n;d1[++c1]=n%,n/=);
int l=,r=,m=(l+r)>>;
while(r>l)
{
if(dfs(c1,m,)->=k)r=m;
else l=m+;
m=(l+r)>>;
}
// the kth sum is m-1
int sum=--m;
ll dx=k-(dfs(c1,m,)-);
memset(d2,-,sizeof(d2));
while(sum>)
{
for(d2[c2]=min(sum,);d2[c2]>=;--d2[c2])
if(dx>dfs2(c1,,m,,,))break;
sum-=d2[c2++];
}
if(dfs2(c1,,m,,,)!=dx)
{
bool flag=false;
if(c2>&&d2[c2-]==&&d2[c2-]<)
{
ll t=d2[c2-];
d2[--c2-]+=t,d2[c2]=-;
if(dfs2(c1,,m,,,)==dx)flag=true;
else d2[c2-]-=t,d2[c2++]=t;
}
if(!flag)
do{d2[c2++]=;}while(dfs2(c1,,m,,,)!=dx);
}
ll ans=;
for(int i=;i<c2;++i)
ans*=,ans+=d2[i];
return ans;
}
int main()
{
ll n,m;
memset(dp,-,sizeof(dp));
memset(dp2,-,sizeof(dp2));
while(~scanf("%lld%lld",&n,&m)&&(n+m))
printf("%lld %lld\n",cal(n,m),cal2(n,m));
return ;
}

ZOJ 2599 Graduated Lexicographical Ordering (数位DP)的更多相关文章

  1. ZOJ 2599 Graduated Lexicographical Ordering ★(数位DP)

    题意 定义两个数的比较方法,各位数字之和大的数大,如果数字和相等则按字典序比较两个数的大小.输入n,k,求:1.数字k的排名:2.排名为k的数. 思路 算是一类经典的统计问题的拓展吧~ 先来看第一问. ...

  2. ZOJ2599:Graduated Lexicographical Ordering(很经典的数位DP)

    Consider integer numbers from 1 to n. Let us call the sum of digits of an integer number its weight. ...

  3. ZOJ 3494 (AC自动机+高精度数位DP)

    题目链接:  http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3494 题目大意:给定一些被禁止的BCD码.问指定范围内不含有 ...

  4. zoj 3962 Seven Segment Display 数位dp

    非常好的一个题,可以比赛时想到的状态太奇葩,不方便转移,就一直没能AC. 思路:dp(i, j)表示已经考虑了前i位,前i位的和为j的贡献.如果当前的选择一直是最大的选择,那么就必须从0~下一位的最大 ...

  5. ZOJ 3494 BCD Code (数位DP,AC自动机)

    题意: 将一个整数表示成4个bit的bcd码就成了一个01串,如果该串中出现了部分病毒串,则是危险的.给出n个病毒串(n<=100,长度<21),问区间[L,R]中有几个数字是不含病毒串的 ...

  6. ZOJ 3494 BCD Code(AC自动机+数位DP)

    BCD Code Time Limit: 5 Seconds      Memory Limit: 65536 KB Binary-coded decimal (BCD) is an encoding ...

  7. ZOJ 3962 Seven Segment Display(数位DP)题解

    题意:给一个16进制8位数,给定每个数字的贡献,问你贡献和. 思路:数位DP,想了很久用什么表示状态,看题解说用和就行,其他的都算是比较正常的数位DP. 代码: #include<iostrea ...

  8. ZOJ 3962 Seven Segment Display(数位DP)

    题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3962 题目大意: 有t组数据. 给你一个n,和8位的十六进制数s ...

  9. ZOJ 3962 E.Seven Segment Display / The 14th Zhejiang Provincial Collegiate Programming Contest Sponsored by TuSimple E.数位dp

    Seven Segment Display Time Limit: 1 Second      Memory Limit: 65536 KB A seven segment display, or s ...

随机推荐

  1. 计算CRC校验值(CRC16和CRC32)(网络传输检验)

    CRC有非常多的模式,我没有全部都做,目前支持 CRC16-Modbus CRC16-X25 CRC32 使用方法 auto data = QByteArray::fromHex( "01 ...

  2. CodeBlocks ubuntu常见问题及小技巧

    1.安装完后没有快捷键的设置: http://blog.csdn.net/sjwbcb/article/details/4797785 感谢这哥,原来是包没有装全. codeblocks用ubuntu ...

  3. 312. Burst Balloons

    题目: Given n balloons, indexed from 0 to n-1. Each balloon is painted with a number on it represented ...

  4. MyBatis学习总结_04_解决字段名与实体类属性名不相同的冲突

    一.准备演示需要使用的表和数据 CREATE TABLE orders( order_id INT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(20), ...

  5. python 细枝末节

    1. print  自动换行 看区别 >>> for i in range(4): ... print i ... 0 1 2 3 >>> for i in ran ...

  6. USACO Section 2.3: Zero Sum

    这题我做得比较麻烦,网上有个比较简单的程序. /* ID: yingzho1 LANG: C++ TASK: zerosum */ #include <iostream> #include ...

  7. 《OD学算法》排序之冒泡排序

    冒泡排序 一语中的:丢一把沙子,轻的物体往上浮. 基本思想:通过无序区中相邻记录关键字间的比较和位置的交换,使关键字最小的记录如气泡一般逐渐往上“漂浮”直至“水面”. 代码示例: import jav ...

  8. [Android教程]EditText设置/隐藏光标位置、选中文本和获取/清除焦点

      有时候需要让光标显示在EditText的指定位置或者选中某些文本.同样,为了方便用户输入以提升用户体验,可能需要使EditText获得或失去焦点. 1. 设置光标到指定位置 EditText et ...

  9. UVa 12206 (字符串哈希) Stammering Aliens

    体验了一把字符串Hash的做法,感觉Hash这种人品算法好神奇. 也许这道题的正解是后缀数组,但Hash做法的优势就是编码复杂度大大降低. #include <cstdio> #inclu ...

  10. HDU 3294 (Manacher) Girls' research

    变形的求最大回文子串,要求输出两个端点. 我觉得把'b'定义为真正的'a'是件很无聊的事,因为这并不会影响到最大回文子串的长度和位置,只是在输出的时候设置了一些不必要的障碍. 另外要注意一下原字符串s ...