题目大意:
将 1~N(1<=N<= 15 10 )写在纸上,然后在相邻的数字间交替插入”+”和”-“,求最后的
结果。例如当 N 为 12 时,答案为:+1-2+3-4+5-6+7-8+9-1+0-1+1-1+2=5
解法分析:
这是一道稍微复杂一点的数位计数问题。
根据上述原则,我们首先探查数位确定,所有数字自由的情况。
若数位数为偶数,以 6 位为例(不妨设第一个符号为+):
+0 -0 +0 -0 +0 -0
+0 -0 +0 -0 +0 -1
+0 -0 +0 -0 +0 -2
………………………………………………………
+9 -9 +9 -9 +9 -9
此时,每一个数位的符号都是确定的,因此只需要分别计算每一位的所有数字和即可,
因为所有位 0~9 出现机会均等,因此和必然为 0。
若数位数为奇数,此情况更加简单,以 5 位为例(不妨设第一个符号为+) :
+0 -0 +0 -0 +0
-0 +0 -0 +0 -1
+0 -0 +0 -0 +2
-0 +0 -0 +0 -3
………………………………………………………
+9 -9 +9 -9 +8
-9 +9 -9 +9 -9
可以注意到,相邻两行的和必然为-1,因此整个和很容易求出。
于是我们编写函数 getsum1 和检验函数 check。 (getsum1 的另一个参数 k 的由来在下页
有说明)
long long getsum1( int n, int k )
//n 为自由位个数,k 为总位数(k>=n>=1)
{
if ( k % 2 == 0 )
{
if ( n % 2 == 0 ) return 0;
else
{
long long d = -45;
for (int i = 0; i < n - 1; i++ ) d *= 10;
return d;
}
}
else
{
long long d = -1;
for (int i = 0; i < n; i++ ) d *= 10;
return d / 2;
}}
long long check( int n )
{
long long ret = 0;
int t = 1, a[10];
for (int i = 1; i <= n; i++ )
{
int r = 0, p = i;
while ( p > 0 )
{
a[r++] = p % 10;
p /= 10;
}
for (int j = r - 1; j >= 0; j-- )
{
ret += a[j] * t;
t = -t;
}
}
return ret;
}
接下来,考虑带有前缀的情况,因为前缀对符号的影响,所以需要在 getsum1 处追加总
位数的参数。此时由 getsum1 处可求出自由位的数字和,因此只需再求出前缀的数字和即可。
当总位数=自由位+前缀位数为偶数时:
+1 -2 +0 -0 +0 -0
+1 -2 +0 -0 +0 -1
+1 -2 +0 -0 +0 -2
………………………………………………………
+1 -2 +9 -9 +9 -9
前缀数字和符号不变,因此只需要乘总行数即可。
总位数为奇数时:
+1 -2 +0 -0 +0
-1 +2 -0 +0 -1
+1 -2 +0 -0 +2
-1 +2 -0 +0 -3
清华附中 高逸涵
第 8 页 共 13 页
………………………………………………………
+1 -2 +9 -9 +8
-1 +2 -9 +9 -9
前缀两两相消,和为 0。
依照以上分析编写 getsum2。
long long getsum2( long long prefix, int n )
//prefix 为前缀,n 为自由位个数(n >= 1, prefix >= 1)
{
int d = 0, t = 1;
long long p = prefix, presum = 0;
while ( p > 0 )
{
presum += (p % 10) * t;
p /= 10;
d ++;
t = -t;
}
presum *= -t;
for (int i = 0; i < n; i++ ) presum *= 10;
long long ret = getsum1( n, n + d );
if ( (d + n) % 2 == 0 ) ret += presum;
return ret;
}
沿用上例的思路,再有了上述两个函数之后,我们继续将整个区间划分为若干段,分别
利用上述函数求和,这里不再重复叙述,只是将函数实现展示如下。(不能沿用上例递归程
序是由于上例添加前导 0 对结果不影响,而本例则不同)
long long getsum3( long long n )
//对原问题进行求和[1,n],n>=1
{
if ( n < 10 )
{
long long ret = 0;
for (int i = 1; i <= n; i++ )
if ( i % 2 == 0 ) ret -= i; else ret += i;
return ret;
}
long long tn = n, p = 1;
int d = 0;
while ( tn > 0 )
{
tn /= 10;
d ++;
}
for (int i = 1; i < d; i++ ) p *= 10;
long long prefix = 0, ret = 5;
for (int j = 1; j < d - 1; j++ )
for (int i = 1; i <= 9; i++ )
ret -= getsum2( i, j );
tn = n;
while (d > 1)
{
for (int i = 0; i < tn / p; i++ )
{
if ( prefix != 0 )
ret -= getsum2( prefix, d - 1 );
prefix ++;
}
tn %= p; p /= 10;
d--; prefix *= 10;
}
int a[20], t = -1;
for (int i = 0; i <= tn; i++ )
{
long long p = prefix + i;
int r = 0;
while ( p > 0 )
{
a[r++] = p % 10;
p /= 10;
}
for (int j = r - 1; j >= 0; j-- )
{
ret += a[j] * t;
t = -t;
}
}
return ret;
}
AC代码如下:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
long long getsum1( long long n, long long k ){
if( k % 2 == 0 ){
if( n % 2 == 0 ){
return 0;
}else{
long long ans = -45;
for( int i = 1; i < n; i++ ){
ans *= 10;
}
return ans;
}
}else{
long long ans = -1;
for( int i = 1; i <= n; i++ ){
ans *= 10;
}
return ans / 2;
}
}
long long getsum2( long long prefix, long long n ){
long long d = 0, t = 1;
long long p = prefix, presum = 0;
long long ans = 0;
while( p > 0 ){
presum += ( p % 10 ) * t;
p /= 10;
d++;
t = -t;
}
presum *= -t;
for( int i = 1; i <= n; i++ ){
presum *= 10;
}
ans += getsum1( n, n + d );
if( ( n + d ) % 2 == 0 ){
ans += presum;
}
return ans;
}
long long getsum3( long long n ){
if( n < 10 ){
long long ans = 0;
for( int i = 1; i <= n; i++ ){
if( i % 2 == 0 ){
ans -= i;
}else{
ans += i;
}
}
return ans;
}
long long ans = 5;
long long d = 0, tn = n, p = 1;
while( tn > 0 ){
tn /= 10;
d++;
}
for( int j = 1; j < d - 1; j++ ){
for( int i = 1; i <= 9; i++ ){
ans -= getsum2( i, j );
}
}
for( int i = 1; i < d; i++ ){
p *= 10;
}
tn = n;
long long prefix = 0;
while( d > 1 ){
for( int i = 0; i < tn / p; i++ ){
if( prefix != 0 ){
ans -= getsum2( prefix, d - 1 );
}
prefix++;
}
prefix *= 10;
tn %= p;
p /= 10;
d--;
}
int a[20], r = 0, t = -1;
for( int i = 0; i <= tn; i++ ){
long long p = prefix + i;
r = 0;
while( p > 0 ){
a[r++] = p % 10;
p /= 10;
}
for( int j = r - 1; j >= 0; j-- ){
ans += a[j] * t;
t = -t;
}
}
return ans;
}
int main(){
long long n;
while( cin >> n, n ){
cout << getsum3( n ) << endl;
}
return 0;
}