 /**//*
一个数是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
搜索
最新评论

|
|