Gym 102040B Counting Inversion(超级数位dp)
问在x,y范围内Σyk=xDI(K)为多少?
DI(K)定义为di>dj&&i<j ,K由dn....d2d1d0构成如DI(123) = 3(d2<d1,d2<d0,d1<d0)DI(253) = 2(d2<d0,d1<d0)
那么我们就数位dp一下,访问所有的数,h[cur][flag][limit]为从状态(u,flag,limit)向后走能到达的状态总数,num[u][flag][limit][i]为状态(cur,flag,limit)及其向后走能到达的所有状态中数字ii出现的总数
#include<bits/stdc++.h> /*#include<cstring> #include<set> #include<map> #include<iostream> #include<algorithm> #if(__cplusplus == 201103L) #include <unordered_map> #include <unordered_set> #else #include <tr1/unordered_map> #include <tr1/unordered_set> namespace std { using std::tr1::unordered_map; using std::tr1::unordered_set; } #endif*/ #define ll long long #define mem(a,b) memset(a,b,sizeof a) #define pb push_back #define de(x) cout<<x<<endl #define dd(x) cout<<x<<" " #define is insert #define PI acos(-1) #define ull unsigned long long using namespace std; const int maxn =2e5+3; const int mod = 1e9+7; typedef pair<ll, ll> p1; typedef pair< ll, pair<ll,ll > >p2; inline int rd(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;} void out(int a) {if(a<0)putchar('-'),a=-a;if(a>=10)out(a/10);putchar(a%10+'0');} const int INF = 0x3f3f3f3f; const ull base = 131; int MOVE[4][2]={ {0,1}, {1,0}, {0,-1}, {-1,0}, }; ll ans[20],f,vis[20][2][2],dp[20][2][2],num[20][2][2][11],h[20][2][2],tt;//vis数组是为了防止重复访问,num数组记录每一状态下(0-9)出现的个数h为从状态(cur,flag,limit)向后走能到达的状态总数,dp统计所要求的权值 void dfs(int cur,bool flag,bool limit)//cur表示位数,flag表示是否有前缀0,limit是否为最大 { if(vis[cur][flag][limit] == tt) return;//如果已经访问过了那么就弹出循环 vis[cur][flag][limit] = tt;//还没访问过就标记一下 dp[cur][flag][limit] = 0;//对于每一层dp进行清零 for(int i=0;i<=9;i++)//对于每层num数组进行清零 { num[cur][flag][limit][i] = 0; } if(cur == 0) { h[cur][flag][limit] = 1; return; } h[cur][flag][limit] = 0; int up = limit?ans[cur]:9;//最大遍历上限 for(int i=0;i<=up;i++) { dfs(cur-1,flag&&i==0,limit&&i==up); h[cur][flag][limit] += h[cur-1][flag&&i==0][limit&&i==up]; for(int j=0;j<=9;j++) num[cur][flag][limit][j]+=num[cur-1][flag&&i==0][limit&&i==up][j];//num统计下一层所出现j的个数 dp[cur][flag][limit]+=dp[cur-1][flag&&i==0][limit&&i==up]; if(!(flag&&(i==0)))//num相加下一层所拥有的数,然后汇总在dp数组上 { for(int j=0; j<=i; ++j)num[cur][flag][limit][j]+=h[cur-1][flag&&i==0][limit&&i==up]; dp[cur][flag][limit]+=num[cur-1][flag&&i==0][limit&&i==up][i+1]; } } } ll solve(ll x) { // mem(dp,0); // mem(vis,0); // mem(h,0); // mem(num,0); int count = 0; ++tt; // 把所有数字存入数组中 while(x) { ans[++count] = x%10; x/=10; } dfs(count,1,1); return dp[count][1][1]; } void run() { ll a,b; cin >>a>>b; printf("Case %lld: %lld\n",f,solve(b)-solve(a-1)); f++; //cout<<tt<<endl; } signed main() { //std::ios::sync_with_stdio(false); int t = rd(); f = 1; while(t--) run(); }