【HDU 4352】 XHXJ's LIS (数位DP+状态压缩+LIS)

XHXJ's LIS

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2422    Accepted Submission(s): 990


Problem Description
#define xhxj (Xin Hang senior sister(学姐)) 
If you do not know xhxj, then carefully reading the entire description is very important.
As the strongest fighting force in UESTC, xhxj grew up in Jintang, a border town of Chengdu.
Like many god cattles, xhxj has a legendary life: 
2010.04, had not yet begun to learn the algorithm, xhxj won the second prize in the university contest. And in this fall, xhxj got one gold medal and one silver medal of regional contest. In the next year's summer, xhxj was invited to Beijing to attend the astar onsite. A few months later, xhxj got two gold medals and was also qualified for world's final. However, xhxj was defeated by zhymaoiing in the competition that determined who would go to the world's final(there is only one team for every university to send to the world's final) .Now, xhxj is much more stronger than ever,and she will go to the dreaming country to compete in TCO final.
As you see, xhxj always keeps a short hair(reasons unknown), so she looks like a boy( I will not tell you she is actually a lovely girl), wearing yellow T-shirt. When she is not talking, her round face feels very lovely, attracting others to touch her face gently。Unlike God Luo's, another UESTC god cattle who has cool and noble charm, xhxj is quite approachable, lively, clever. On the other hand,xhxj is very sensitive to the beautiful properties, "this problem has a very good properties",she always said that after ACing a very hard problem. She often helps in finding solutions, even though she is not good at the problems of that type.
Xhxj loves many games such as,Dota, ocg, mahjong, Starcraft 2, Diablo 3.etc,if you can beat her in any game above, you will get her admire and become a god cattle. She is very concerned with her younger schoolfellows, if she saw someone on a DOTA platform, she would say: "Why do not you go to improve your programming skill". When she receives sincere compliments from others, she would say modestly: "Please don’t flatter at me.(Please don't black)."As she will graduate after no more than one year, xhxj also wants to fall in love. However, the man in her dreams has not yet appeared, so she now prefers girls.
Another hobby of xhxj is yy(speculation) some magical problems to discover the special properties. For example, when she see a number, she would think whether the digits of a number are strictly increasing. If you consider the number as a string and can get a longest strictly increasing subsequence the length of which is equal to k, the power of this number is k.. It is very simple to determine a single number’s power, but is it also easy to solve this problem with the numbers within an interval? xhxj has a little tired,she want a god cattle to help her solve this problem,the problem is: Determine how many numbers have the power value k in [L,R] in O(1)time.
For the first one to solve this problem,xhxj will upgrade 20 favorability rate。
Input
First a integer T(T<=10000),then T lines follow, every line has three positive integer L,R,K.(
0<L<=R<263-1 and 1<=K<=10).
Output
For each query, print "Case #t: ans" in a line, in which t is the number of the test case starting from 1 and ans is the answer.
Sample Input
1 123 321 2
Sample Output
Case #1: 139
 
 
【题意】
  
  求x到y之间有多少个数的最长上升子序列长度为k(x<=y<=10^63-1)(将一个数看作一个字符串)数据组数:T<=10000
 
【分析】
 
  

  首先要看出来是数位DP。特点是什么呢,就是跟数的每一位的数字有关。
  

    然后要会LIS的nlogn解法,记录f[i]为当前序列长度为i的上升序列的最后一位最小是什么(因为我们知道最后一位尽量小可能结果更优),当我们新加入一个数x,我们是尝试把它放在f[i]表示的序列后面,看看是不是比f[i+1]优,那么就是找一个f[i]<x<f[i+1]的位置,又因为f数组有单调性(单调不减),所以这样的位置最多只有一个,所以普通的LIS就是二分求出这个位置。
  

    这一题的LIS最长是10,所以我们可以位压直接记录f数组,f数组的size就是当前状态的LIS,并且记录了f就可以保证后面的转移不会错。
   然后就数位DP。这次打的数位DP跟以前有点不一样,这题填了数之后对后面的填数还是有影响的,所以不能初始化了。但是可以记忆化搜索,当你的ans与原来的限制数无关(就代码中的flag=0),就可以记录下当前k情况下的ans,那么就省掉了很多了。(猴塞雷啊)
  前导0要记得考虑它对状态的影响!

  DP[X][S][K]表示当前要算长度为K的LIS,还能填X个数,状态为S(f数组的状压)的方案数,注意flag!=0时才放进去。

代码如下:

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 #include<iostream>
 5 #include<algorithm>
 6 #include<queue>
 7 #include<cmath>
 8 using namespace std;
 9 #define LL long long
10 
11 int siz[1<<11],nex[1<<11][10];
12 
13 int ffind(int x,int y)
14 {
15     for(int i=y;i<10;i++) if((1<<i)&x)
16       return x-(1<<i)+(1<<y);
17     return x+(1<<y);
18 }
19 
20 void init()
21 {
22     for(int i=0;i<(1<<10);i++)
23     {
24         int x=i;siz[i]=0;
25         while(x) siz[i]+=(x&1)?1:0,x>>=1;
26         for(int j=0;j<10;j++)
27         {
28             nex[i][j]=ffind(i,j);
29         }
30     }
31 }
32 
33 int a[10],c,k;
34 LL f[20][1<<10][10];
35 
36 LL get_f(int n,int s,bool zero,bool flag)
37 {
38     if(n==0) return (siz[s]==k);
39     if(!flag&&f[n][s][k]!=-1) return f[n][s][k];
40     int ed=flag?a[n]:9;
41     LL ans=0;
42     for(int i=0;i<=ed;i++)
43         ans+=get_f(n-1,(zero&&(i==0))?0:nex[s][i],zero&&(i==0),flag&&(i==ed));
44     if(!flag) f[n][s][k]=ans;
45     return ans;
46 }
47 
48 LL get_ans(LL n)
49 {
50     c=0;
51     while(n) a[++c]=n%10,n/=10;
52     return get_f(c,0,1,1);
53 }
54 
55 int main()
56 {
57     int T,kase=0;
58     scanf("%d",&T);
59     init();
60     memset(f,-1,sizeof(f));
61     while(T--)
62     {
63         LL l,r;
64         scanf("%lld%lld%d",&l,&r,&k);
65         LL ans=get_ans(r)-get_ans(l-1);
66         printf("Case #%d: %lld
",++kase,ans);
67     }
68     return 0;
69 }
[HDU 4352]

  做了这题才发现我没有真的会LIS啊,虽然之前用数据结构也可以做到nlogn,但是套这题就不行了,所以放一下另一个LIS的nlogn解法~~

最长上升子序列nlogn算法

在川大oj上遇到一道题无法用n^2过于是,各种纠结,最后习得nlogn的算法

最长递增子序列,Longest Increasing Subsequence 下面我们简记为 LIS。
排序+LCS算法 以及 DP算法就忽略了,这两个太容易理解了。

假设存在一个序列d[1..9] = 2 1 5 3 6 4 8 9 7,可以看出来它的LIS长度为5。n
下面一步一步试着找出它。
我们定义一个序列B,然后令 i = 1 to 9 逐个考察这个序列。
此外,我们用一个变量Len来记录现在最长算到多少了

首先,把d[1]有序地放到B里,令B[1] = 2,就是说当只有1一个数字2的时候,长度为1的LIS的最小末尾是2。这时Len=1

然后,把d[2]有序地放到B里,令B[1] = 1,就是说长度为1的LIS的最小末尾是1,d[1]=2已经没用了,很容易理解吧。这时Len=1

接着,d[3] = 5,d[3]>B[1],所以令B[1+1]=B[2]=d[3]=5,就是说长度为2的LIS的最小末尾是5,很容易理解吧。这时候B[1..2] = 1, 5,Len=2

再来,d[4] = 3,它正好加在1,5之间,放在1的位置显然不合适,因为1小于3,长度为1的LIS最小末尾应该是1,这样很容易推知,长度为2的LIS最小末尾是3,于是可以把5淘汰掉,这时候B[1..2] = 1, 3,Len = 2

继续,d[5] = 6,它在3后面,因为B[2] = 3, 而6在3后面,于是很容易可以推知B[3] = 6, 这时B[1..3] = 1, 3, 6,还是很容易理解吧? Len = 3 了噢。

第6个, d[6] = 4,你看它在3和6之间,于是我们就可以把6替换掉,得到B[3] = 4。B[1..3] = 1, 3, 4, Len继续等于3

第7个, d[7] = 8,它很大,比4大,嗯。于是B[4] = 8。Len变成4了

第8个, d[8] = 9,得到B[5] = 9,嗯。Len继续增大,到5了。

最后一个, d[9] = 7,它在B[3] = 4和B[4] = 8之间,所以我们知道,最新的B[4] =7,B[1..5] = 1, 3, 4, 7, 9,Len = 5。

于是我们知道了LIS的长度为5。

!!!!! 注意。这个1,3,4,7,9不是LIS,它只是存储的对应长度LIS的最小末尾。有了这个末尾,我们就可以一个一个地插入数据。虽然最后一个d[9] = 7更新进去对于这组数据没有什么意义,但是如果后面再出现两个数字 8 和 9,那么就可以把8更新到d[5], 9更新到d[6],得出LIS的长度为6。

然后应该发现一件事情了:在B中插入数据是有序的,而且是进行替换而不需要挪动——也就是说,我们可以使用二分查找,将每一个数字的插入时间优化到O(logN)~~~~~于是算法的时间复杂度就降低到了O(NlogN)~!

转自:http://blog.csdn.net/shuangde800/article/details/7474903


至于数位DP,以后再说吧,这样子的记忆化搜索式的数位DP感觉还不错(虽然我以前不是这样打),我现在也没有Y出dfs打法有什么局限性,速度也是很好的。

LL get_f(int n,int s,bool zero,bool flag)
{
	if(n==0) return (siz[s]==k);
	if(!flag&&f[n][s][k]!=-1) return f[n][s][k];
	int ed=flag?a[n]:9;
	LL ans=0;
	for(int i=0;i<=ed;i++)
		ans+=get_f(n-1,(zero&&(i==0))?0:nex[s][i],zero&&(i==0),flag&&(i==ed));
	if(!flag) f[n][s][k]=ans;
	return ans;
}

2016-10-08 17:08:59

原文地址:https://www.cnblogs.com/Konjakmoyu/p/5939357.html