码迷,mamicode.com
首页 > 其他好文 > 详细

HDU 4352 XHXJ's LIS

时间:2020-01-02 20:18:35      阅读:71      评论:0      收藏:0      [点我收藏+]

标签:reg   g++   cas   处理   case   lld   字符串   cal   ret   

题目

题意:定义一个数字的LIS为将其看成一个字符串的LIS,问\([l,r]\)内有多少数字的LIS长度为\(k\)

考虑树状数组求一个序列LIS的过程,我们定义了一个东西\(f_i\)表示结尾元素不超过\(i\)的LIS的最大长度,维护这个数组就能实现较为快速的转移

显然这个数组是单调不降的,且不难发现\(f_i-f_{i-1}\leq 1\),于是差分之后就只会存在\(0,1\)两种元素

于是我们在数位dp的过程中把差分序列记下来作为dp的状态就好了,注意前导零的问题,以及预处理转移会更快

代码

#include<cstdio>
#include<cstring>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
LL L,R;int k,m,lm[66];
int id[(1<<10)+5],st[11][(1<<10)+5],cnt[(1<<10)+5];
LL dp[2][(1<<10)+5][2];
int A[(1<<10)+5][11],tr[(1<<10)+5][11],B[11],M[11];
inline void trans(int state) {
    for(re int i=0;i<10;++i)A[m][i]=state>>i&1;
    for(re int i=1;i<10;++i)A[m][i]+=A[m][i-1]; 
}
inline void Pre_tr(int x) {
    for(re int i=0;i<10;i++) {
        for(re int j=0;j<10;j++)B[j]=A[x][j];
        B[i]=max(B[i],(i>0?A[x][i-1]:0)+1);
        for(re int j=1;j<10;j++)B[j]=max(B[j-1],B[j]);
        tr[x][i]=B[0];
        for(re int j=1;j<10;j++) tr[x][i]|=((B[j]-B[j-1])<<j);
        tr[x][i]=id[tr[x][i]];
    }
}
void dfs(int x,int state,int res) {
    if(res<0) return;
    if(x==10) {
        id[state]=++m;trans(state);
        cnt[m]=res;st[res][++M[res]]=m;
        return;
    }
    dfs(x+1,state,res);
    dfs(x+1,state|(1<<x),res+1);
}
inline LL calc(LL N) {
    if(!N)return 0;int tot=0;
    while(N) lm[++tot]=N%10ll,N/=10ll;
    int o=0;memset(dp,0,sizeof(dp));
    for(re int i=tot;i;--i,o^=1) {
        dp[o][id[0]][i==tot?1:0]++;
        for(re int j=1;j<=m;j++) dp[o^1][j][0]=dp[o^1][j][1]=0;
        for(re int j=1;j<=m;j++) {
            if(!dp[o][j][0]) continue;
            if(cnt[j]>=k) continue;
            for(re int k=0;k<10;++k)
                if(tr[j][k]) dp[o^1][tr[j][k]][0]+=dp[o][j][0];
        }
        for(re int j=1;j<=m;j++) {
            if(!dp[o][j][1]) continue;
            if(cnt[j]>=k) continue;
            for(re int k=0;k<lm[i];++k)
                if(tr[j][k]) dp[o^1][tr[j][k]][0]+=dp[o][j][1];
            if(tr[j][lm[i]]) dp[o^1][tr[j][lm[i]]][1]+=dp[o][j][1];
        }
        dp[o^1][tr[id[0]][0]][lm[i]?0:1]--;
    }
    LL ans=0;
    for(re int i=1;i<=M[k];i++) ans+=dp[o][st[k][i]][0]+dp[o][st[k][i]][1];
    return ans;
}
int main() {
    m=0;dfs(0,0,0);
    for(re int i=1;i<=m;i++) Pre_tr(i);
    int T;scanf("%d",&T);
    for(re int tnum=1;tnum<=T;++tnum) {
        scanf("%lld%lld%d",&L,&R,&k);
        printf("Case #%d: %lld\n",tnum,calc(R)-calc(L-1));
    }
    return 0;
}

之后这玩意就T没了,原因是\(T\leq 10^4\),实在是不小;于是改成记忆化才行

#include<cstdio>
#include<cstring>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
LL L,R;int k,m,lm[66];
int id[(1<<10)+5],cnt[(1<<10)+5];
LL dp[20][(1<<10)+5][11];
int A[(1<<10)+5][11],tr[(1<<10)+5][11],B[11],M[11];
inline void trans(int state) {
    for(re int i=0;i<10;++i)A[m][i]=state>>i&1;
    for(re int i=1;i<10;++i)A[m][i]+=A[m][i-1]; 
}
inline void Pre_tr(int x) {
    for(re int i=0;i<10;i++) {
        for(re int j=0;j<10;j++)B[j]=A[x][j];
        B[i]=max(B[i],(i>0?A[x][i-1]:0)+1);
        for(re int j=1;j<10;j++)B[j]=max(B[j-1],B[j]);
        tr[x][i]=B[0];
        for(re int j=1;j<10;j++) tr[x][i]|=((B[j]-B[j-1])<<j);
        tr[x][i]=id[tr[x][i]];
    }
}
void dfs(int x,int state,int res) {
    if(res<0) return;
    if(x==10) {
        id[state]=++m;trans(state);cnt[m]=res;
        return;
    }
    dfs(x+1,state,res);
    dfs(x+1,state|(1<<x),res+1);
}
LL DFS(int len,int lim,int s) {
    if(!len||cnt[s]>k) return cnt[s]==k;
    if(!lim&&dp[len][s][k]!=-1) return dp[len][s][k];
    LL ans=0;
    for(re int i=0;i<=(lim?lm[len]:9);++i)
        ans+=DFS(len-1,lim&&(i==lm[len]),tr[s][i]);
    if(!lim) dp[len][s][k]=ans;
    return ans;
}
inline LL calc(LL N) {
    if(!N)return 0;int tot=0;
    while(N) lm[++tot]=N%10ll,N/=10ll;
    return DFS(tot,1,id[0]);
}
int main() {
    m=0;dfs(0,0,0);memset(dp,-1,sizeof(dp));
    for(re int i=1;i<=m;i++) Pre_tr(i);tr[id[0]][0]=id[0];
    int T;scanf("%d",&T);
    for(re int tnum=1;tnum<=T;++tnum) {
        scanf("%lld%lld%d",&L,&R,&k);
        printf("Case #%d: %lld\n",tnum,calc(R)-calc(L-1));
    }
    return 0;
}
//g++ hdu4352.cpp -o hdu4352

HDU 4352 XHXJ's LIS

标签:reg   g++   cas   处理   case   lld   字符串   cal   ret   

原文地址:https://www.cnblogs.com/asuldb/p/12141735.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!