B.Counting Inversion(超级数位dp) 20182019-acmicpc-asia-dhaka-regional

题目大意:

        求x~y这些数字中,数位组成的逆序对个数。

题目思路:

       首先想到的是数位dp,但是我们发现要求的东西非常多,先考虑线性dp,dp[ i ][ j ]表示第 i 位填入 j 后往后能构成的逆序对个数,如何转移呢,肯定是先又dp[ i-1 ]的合法状态转移过来,再加上dp【i】这一位本身的贡献了多少逆序对,加入第 i 位填入了 j,

那么后边要出现比这一位大的就可以,但是当我们打算预处理这个东西的时候发现有些东西好像不对劲,我们还是要讨论一些关于limit的问题,此时又需要一个数位dp,在计算第i位填入j ,它本身参与的会产生多少逆序对。

       这个怎么求,当第i位填入了j,那么后边如果某位填入了比j大的,剩下的往后的 j 位就可以取所有可能,这时又需要一个预处理来求后j位有多少种可能,只有两种情况要么是这一位被limit要么是不limit。

      (昊妈牛逼!!!)

         dfs  就是正常的dp过程

         dfs1 就是求第i位填j并且j参与进来的,所产生的逆序对个数。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll x,y;
ll a[20],dp[20][20][20];
ll num[20][10][10],sum[20][10];
ll dfs2(int pos ,int limit,int x)
{
    if(pos == -1)return 0;
    if(num[pos][limit][x] != -1)return num[pos][limit][x];
    int up = (limit)? a[pos]:9;
    ll ret = 0;
    for(int i=0;i<=up;i++){
        ret += dfs2(pos-1,limit&&i==a[pos],x);
        if(i>x){
            ret += sum[pos][ limit && i==a[pos] ] ;
        }
    }
    return num[pos][limit][x] = ret;
}
ll dfs(int pos , int head ,int limit)
{
    if(pos == -1)return 0;
    if(dp[pos][head][limit] != -1)return dp[pos][head][limit];
    int up =(limit)?a[pos]:9;
    ll ret = 0;
    for(int i=0;i<=up;i++){
        ret += dfs(pos-1,head && i==0 ,limit && i==a[pos]);
        if(!(head && i==0)){
            ret  += dfs2(pos-1,limit && i==a[pos],i);
        }
    }
    return dp[pos][head][limit] = ret;
}
ll cal(ll x)
{
    memset(dp,-1,sizeof(dp));
    memset(num,-1,sizeof(num));
    memset(sum,0,sizeof(sum));
    int tot = 0;
    while(x){
        a[tot++] = x%10;
        x/=10;
    }
    ll s1 = 1,s2 = 0,wei = 1;
    for(int i=0;i<tot;i++){
        sum[i][1] = (i==0)?1:(s2+1);
        sum[i][0] = (i==0)?1:s1;
        s1 *= 10;s2 += wei*a[i];wei *=10;
    }
    return dfs(tot-1,1,1);
}
int main()
{
    int t,s=0;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%I64d%I64d",&x,&y);
        printf("Case %d: %I64d\n",++s,cal(y)-cal(x-1));
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值