首先要吐两行槽:看到集训队论文上有这道题,由于数位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. 配置Tomcat的服务端口

  2. [iOS]URL编码和解码

    1. 首先来看下什么样的是URL编码(字符串中带有%22 类似这样的) NSString *str = @"http://m.tuniu.com/api/home/data/index/c/ ...

  3. MyBatis学习总结_08_Mybatis3.x与Spring4.x整合

    一.搭建开发环境 1.1.使用Maven创建Web项目 执行如下命令: mvn archetype:create -DgroupId=me.gacl -DartifactId=spring4-myba ...

  4. 关于Netty4.x中文教程系列更新进度的说明和道歉

    最近一些事情.貌似发现很久没更新教程了.这里和大家说一声对不起.教程5的前半部分差不多年前就写好了.但是由于年前我在的项目组项目进度比较紧张.一直在加班.教程的后半部分就一直没有写.年后由于一些公司人 ...

  5. 由反汇编C程序来理解计算机是如何工作的

    C语言代码 int g(int x) { return x + 109; } int f(int x) { return g(x); } int main() { return f(122) + 3; ...

  6. 无线路由器WDS设置方法图解_无线桥接设置

    随着无线网络的发展,现在越来越多的公司及企业都已经开始布局无线局域网,今天我们主要介绍下适合中小企业的无线路由器桥接或WDS功能.文章以TP-link WR841N无线路由器设置为例,其它路由器参考设 ...

  7. Android开发环境的安装 Eclipse

    Android开发环境的安装 1 IDE Android可以使用开发的IDE有Eclipse 或者 Android Studio.Android Studio还处于v 0.1.x版本,是early a ...

  8. combobox远程加载数据的总结和Json数据的小结

    1.从后台返回请求加载Combobox下拉框数据 html部分1 <select name="mateBelongZ" id="mateBelongZID" ...

  9. 使用jQuery插件PrintArea进行html页面打印

    在开发系统时,我们一般会有统计分析的功能需求,而现在大多数系统都是使用B/S的形式开发. 所以,html报表打印,并且打印指定内容,成了必要的功能. 开源地址:https://github.com/R ...

  10. 反编译android APK

    我们经常会在如下的情况使用反编译 1.看到别人应用中的酷炫功能,想知道是如何实现的 2.别人应用的素材排版好漂亮,想套用模仿   百度一下就已经有一大堆反编译的教程了,我还是坚持学习记录一下.   A ...