/**//* 一个数是Balanced ,指这个数的力矩为0 如 4139 选支点为3 4*2 + 1*1 = 9*1 为[x,y]内有多少个Balanced Number 0<=x<=y<=10^18
watashi的博客里提到如果 <=10^9 用 sqrt(n)的方法打表,打印每10w个数之间有多少个Balanced Number 感觉这种方法很好 sqrt(n)
这题数据规模很大10^18,要用数位统计
dp的预处理我想的少了一位,最后问别人 dp[all,len,total] 表示总长度为all,字符占的长度为len,力矩为total的方案数 如xxx123 即是 dp[6,3,1*3+2*4+3*5]
init后,就用数位统计cal(x)统计[0,x]内多少个Balanced Number 枚举位数,枚举该位小于bit[i]的数字j 然后再枚举支点位置,1到len 然后统计 (枚举的位置是1到len,第一次遇到枚举的位置可以是pre的东西)
由于一个数的支点只有一个!!!所以不会重复计数 “就会发现一个数最多关于一个digit是balanced的(一个严格单调函数最多有一个零点)。 注意到了这一点就会知道,这题是不存在重复计算这种问题的。”
但注意的是0的支点不止一个,所以要减去重复的0 000000 支点任意一个都可以 */ #include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<queue> #include<map> #include<cmath> #include<cstdlib> #include<cassert> #include<iostream>
using namespace std;
const int INF = 1000000000;
long long _dp[2][200][2000];//len sum total long long dp[20][20][2000];//all len total
bool debug = true;
void init() { _dp[0][0][0] = 1; for(int all = 0 ; all <= 19 ; all++) dp[all][0][0] = 1; int pre = 0 , now = 1; for(int len = 1 ; len <= 19 ; len++)//要算到19位!! { int sum = len * 9; int total = (len+1)*len/2*9; //clear for(int s = 0 ; s <= sum ; s++) for(int t = 0; t <= total ; t++) _dp[now][s][t] = 0;
int _sum = (len-1)*9; int _total = (len-1)*len/2*9; for(int s = 0 ; s <= _sum ; s++) for(int t = 0 ; t <= _total ; t++) for(int j = 0 ; j <= 9 ; j++) { _dp[now][s+j][t+s] += _dp[pre][s][t]; }
for(int x = 0 ; x+len <= 19 ; x++) for(int s = 0 ; s <= sum ; s++) for(int t = 0 ; t <= total ; t++) { dp[len+x][len][t+s*x] += _dp[now][s][t]; }
swap(pre,now); } }
long long cal(long long x)//[0,x] { if(x<0)return 0; x++;
int len = 0 , bit[21]; while(x) { bit[++len] = x % 10; x /= 10; }
long long res = 0; int toRight[21] = {0} , toLeft[21] = {0}; int sum[21] = {0};
for(int i = len ; i ; i--)//[1,_x) _x = x+1 { toRight[i] = toRight[i+1] + sum[i+1]; toLeft[i] = toLeft[i+1] + (len-i)*bit[i]; sum[i] = sum[i+1] + bit[i];
if(debug) printf("%d %d %d %d \n",i , toRight[i] , toLeft[i] , sum[i]);
for(int j = 0 ; j < bit[i] ; j++)//枚举第i位数字j {
//枚举支点位置p for(int p = len ; p > i ; p--) { int balance = toRight[p]; int sub = toLeft[i+1] - toLeft[p] - (len-p)*(sum[i+1] - sum[p]) + (p-i)*j; if(balance >= sub) { if(debug) printf("j %d %d %d %lld\n",j,balance ,sub,dp[p][i-1][balance - sub]); res += dp[p][i-1][balance - sub]; } }
if(debug) printf("> i res %lld\n",res);
//p == i { res += dp[i][i-1][toRight[i]]; }
if(debug) printf("= i res %lld\n",res);
for(int p = i-1 ; p ; p--) { int _balance = toRight[i] + (i-p) * (sum[i+1]+j) ; if(debug) printf("j %d p%d %d\n",j,p,_balance); int total = p*(p+1)/2*9; for(int t = 0 ; t <= total; t++) { if(debug) printf("%d %lld %lld\n",t,dp[i-p][i-p][t] , dp[p][p-1][_balance+t]); if( dp[i-p][i-p][t] && dp[p][p-1][_balance+t] ) res += dp[i-p][i-p][t]*dp[p][p-1][_balance+t]; } } if(debug) printf("<i res %lld\n",res); } } return res - (len-1);//减去重复计数的0 }
bool bruteChk(long long x)//布鲁特—福斯 ^_^ { int left = 0 ,right = 0; int bit[21]={0} , len = 0 , sum[21] = {0}; while(x) { bit[++len] = x % 10; x /= 10; } for(int i = len ; i ; i --) sum[i] = sum[i+1] + bit[i]; for(int i = 1; i <= len ; i++) right += (len-i+1)*bit[i];
for(int i = len ; i ; i--) { right -= (sum[1]-sum[i+1]); left += sum[i+1]; if(left == right)return true; } return left == right; }
int main() {
#ifdef ONLINE_JUDGE #else freopen("in", "r", stdin); #endif
init(); debug = false;
int T; for(scanf("%d",&T) ; T-- ; ) { long long x,y; scanf("%lld%lld",&x,&y); printf("%lld\n",cal(y)-cal(x-1)); } return 0; }
|
|
常用链接
随笔分类
Links
搜索
最新评论
|
|