首先要吐两行槽:看到集训队论文上有这道题,由于数位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. SSIS ->> Data Flow Design And Tuning

    Requirements: Source and destination system impact Processing time windows and performance Destinati ...

  2. Hadoop HDFS文件常用操作及注意事项

    Hadoop HDFS文件常用操作及注意事项 1.Copy a file from the local file system to HDFS The srcFile variable needs t ...

  3. GitHub 教程 in Ubuntu

    Follow these steps to configure github if you are the first time to use Github 1. Sign up a username ...

  4. testNG中@Factory详解

    @Factory注解从字面意思上来讲就是采用工厂的方法来创建测试数据并配合完成测试,其主要应对的场景是:对于某一个测试用例或方法,我们需要输入多个测试数据进行测试,并且这些测试数据可以是有一定关系(可 ...

  5. android下使用smack需引入的包

    compile 'org.igniterealtime.smack:smack-android:4.2.0-alpha1' compile 'org.igniterealtime.smack:smac ...

  6. vc 6.0 连接数据库

    在mysql的文档(C:\Program Files\MySQL\MySQL Server 5.5\lib)里面找到libmysql.dll和libmysql.lib 两个文件,将他们移到 工程文件下 ...

  7. Android 工程在4.0基础上混淆

    Android现在对安全方面要求比较高了,我今天要做的对apk进行混淆,用所有的第三方工具都不能反编译,作者的知识产权得到保障了,是不是碉堡了. 一,首先说明我这是在4.0基础上进行的. 先看看pro ...

  8. iOS-APP中添加启动界面

    iOS,非常高兴的又回到了这个领域.纠结的就业之路...找工作的这段时间里,原本以为是iOS的游戏开发,却一直只有后台的offer,到Android,最后正式上班的时候却被告知调到了iOS开发,当然不 ...

  9. UVa (BFS) The Monocycle

    题目不光要求要到达终点而且要求所走的步数为5的倍数,每个时刻有三个选择,前进,左转弯,右转弯. 所以在vis数组中新增加两个维度即可,vis[x][y][dir][color]表示在(x, y)格子方 ...

  10. 设置sudo不输入密码 sudoers 编辑出错后的补救方法

    一 设置sudo为不需要密码 有时候我们只需要执行一条root权限的命令也要su到root,是不是有些不方便?这时可以用sudo代替.默认新建的用户不在sudo组,需要编辑/etc/sudoers文件 ...