題目大意:c++
給定區間 l rspa
求得區間中有多少個數 數的各個數位裏出現最屢次的數>=數的長度的一半 如2233 3334code
枚舉k在數中出現次數在一半以上 那麼求出的全部方案數中應該減去 兩個數各佔一半的狀況blog
#include <bits/stdc++.h> using namespace std; #define LL long long #define INF 0x3f3f3f3f #define mem(i,j) memset(i,j,sizeof(i)) #define inc(i,l,r) for(int i=l;i<=r;i++) #define dec(i,r,l) for(int i=r;i>=l;i--) #define gcd(i,j) __gcd(i,j); const int N=55; const int mod=1e9+7; const double eps=1e-8; LL dp[N][N][2][2]; // dp[i][j][b1][b2] // i爲數的第i位 j做爲枚舉的k出現次數的相對計數器 // b1=1當前位小於上界 =0則是等於上界 // b2=1到當前位以前全是前導0 =0則不是前導0 LL DP(char t[],int n,int k1,int k2) { mem(dp,0); dp[0][25][0][1]=1; // 初始設25 防止負數 inc(i,0,n-1) inc(j,0,N-1) inc(b1,0,1) inc(b2,0,1) { LL cur=dp[i][j][b1][b2]; inc(nxt,0,9) { // 枚舉下一位 if(k2!=-1) { // 說明枚舉的是2233這種被兩個數各佔一半的狀況 if(b2==0 || nxt!=0) // 下一位不是前導0 if(nxt!=k1 && nxt!=k2) continue; // 但又不是這兩種數 } if(b1==0 && nxt>t[i]-'0') continue; // 當前位已是上界 那麼下一位不能超過上界 bool nb2= b2&(nxt==0); // 當前位是0b2爲1 下一位爲0nxt==0爲1 則nb2爲1 int nj=j; if(nb2==0) { // 下一位不是前導0 if(nxt==k1) nj--; else nj++; } // 是k1就+ 不是就- 最後j=25說明k1恰好一半 若是j<25說明k1超過半數 bool nb1= b1|(nxt<t[i]-'0'); // 當前位以前均爲上界b1=0 下一位爲上界nxt<t[i]-'0'=0 則nb1爲0 dp[i+1][nj][nb1][nb2]+=cur; } } LL res=dp[n][25][1][0]+dp[n][25][0][0]; if(k2==-1) inc(j,0,24) res+=dp[n][j][1][0]+dp[n][j][0][0]; return res; } LL ANS(char t[],int n) { LL res=0; inc(k,0,9) res+=DP(t,n,k,-1); // k出現次數超過一半 如2223 inc(k1,0,9) inc(k2,k1+1,9) // k1 k2各佔一半的 如2323 res-=DP(t,n,k1,k2); return res; } int main() { LL ta,tb; while(~scanf("%lld%lld",&ta,&tb)) { ta--; char a[20],b[20]; int lena=0,lenb=0; while(ta) a[lena++]=ta%10+'0', ta/=10; while(tb) b[lenb++]=tb%10+'0', tb/=10; reverse(a,a+lena); reverse(b,b+lenb); printf("%lld\n",ANS(b,lenb)-ANS(a,lena)); } return 0; }