您的位置:首页 > 其它

HDOJ 4352 XHXJ's LIS

2016-03-15 20:49 411 查看
题意:定义一个数的力量为这个数的最长严格上升子序列长, 找出区间内力量等于k的数,数据范围是long long型

比较明显的数位DP,重点在于怎么找这个最长严格上升子序列。用经典的dp可以做到nlgn求解最长子序列,dp[x]就是长度为x的子序列最后一位的最小值。实际上因为dp[x]是严格递增的,所以可以直接用状压来保存dp[x]中有哪些数,这个问题就解决了。剩下的就是数位dp套路了。

下面代码dp[full][now][mask]
中,full为数位的上限标记,now为位数,mask为状压,n为题目中的k值

/* ***********************************************
Author        :axp
Created Time  :2016/3/15 18:43:27
TASK          :B.cpp
LANG          :C++
************************************************ */

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
using namespace std;

typedef long long ll;
const int N = 20;
int T;
int n;
int ar
;
ll dp[2]
[1<<10][11];

ll f(bool full,int now,int mask);

inline void solve(bool full,int now,int mask,int x,ll &re)
{
if(mask==0 && x==0)
{
re+=f(full,now-1,mask);
return;
}
int t=1<<x;
if(mask<t)
mask|=t;
while((t&mask)==0)t<<=1;
mask-=t;
mask|=1<<x;
re+=f(full,now-1,mask);
}

ll f(bool full,int now,int mask)
{
ll &re=dp[full][now][mask]
;
if(full==0 && re!=-1)return re;
if(now==0)
{
int tot=0;
while(mask)
{
tot++;
mask-=mask&-mask;
}
re=(tot==n);
return re;
}

re=0;
int en=9;
if(full)
{
solve(1,now,mask,ar[now],re);
en=ar[now]-1;
}
for(int i=0;i<=en;i++)
solve(0,now,mask,i,re);
return re;
}

ll work(ll x)
{
int cnt=0;
//if(x==0)ar[++cnt]=0;
while(x)
{
ar[++cnt]=x%10;
x/=10;
}
ll re=f(1,cnt,0);
return re;
}

int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
memset(dp[0],-1,sizeof dp[0]);
scanf("%d",&T);
for(int kase=1;kase<=T;kase++)
{
ll l,r;
scanf("%I64d%I64d%d",&l,&r,&n);
ll ans=work(r)-work(l-1);
printf("Case #%d: %I64d\n",kase,ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: