题目大意:
求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));
}
}