记录编号 100535 评测结果 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
题目名称 [SPOJ 1433] 代数和 最终得分 100
用户昵称 Gravatarcstdio 是否通过 通过
代码语言 C++ 运行时间 0.010 s
提交时间 2014-05-06 11:03:41 内存使用 0.31 MiB
显示代码纯文本
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<set>
#include<vector>
#include<map>
using namespace std;
typedef long long ll;
const int SIZEL=20;
ll pow10[SIZEL]={1};
char s[SIZEL]={0};//就是N
int L;
int p[SIZEL]={0};
ll suff_free(int &osg,int n,int pk,int sk){//前面符号是是osg,共n位,前缀是p[0]开始的pk位,后面sk位随意
	//if(n<1||pk<0||sk<0) return 0;
	//一共pow10[sk]个数,拆出来pow10[sk]*n个数码
	ll ans=0;
	if(!(n&1)||((n&1)&&!sk)){//总位数是偶数,前缀的符号总是一样的
		//而若总位数是奇数而且sk=0,那么就单独算了一次
		//若总位数是奇数而且sk>0,则前缀和相互抵消掉
		int sgn=osg;
		for(int i=0;i<pk;i++){
			ans+=sgn*(p[i])*pow10[sk];
			sgn*=-1;
		}
	}
	//if(!sk) return ans;//如果没有自由位就不用进行下面的计算了
	if(sk){//如果没有自由位就不用折腾了
		if(n&1){//总位数是奇数
			//我们可以按照末位数字两两分组:(01),(23),(45),(67),(89),每一组前面所有数位一定相同于是抵消掉,和是1或者-1
			int sgn=osg*((n&1)?1:-1);//这里计算了第一个(即:前缀00000)数末尾0的符号
			sgn*=-1;//每一组的和
			ans+=sgn*pow10[sk]/2;//自由位贡献的和
		}
		else{//总位数是偶数
			//每一位上都有pow10[sk]个数,这些数中0~9的出现概率均等,因此平均数都是4.5,每一位上和的绝对值都是pow10[sk]*4.5
			//再来考虑符号,如果有偶数个自由位那么所有位的和两两抵销,否则答案就是pow10[sk]*4.5*(最后一位的符号)
			if(sk&1) ans+=pow10[sk]*4.5*osg*((n&1)?1:-1);
		}
	}
	osg*=(sk>0||!(n&1))?1:-1;//改变osg的值
	return ans;
}
ll calc(void){
	ll ans=0;
	int sgn=1;
	//计算:1~999...9,这里9的个数是L-1
	for(int i=1;i<=L-1;i++){
		//计算有i位的,从100...0到999...9
		for(int k=1;k<=9;k++){
			p[0]=k;
			ans+=suff_free(sgn,i,1,i-1);
		}
	}
	for(int i=1;i<=L;i++){//固定第1~i位
		for(int j=0;j<s[i-1]-'0';j++){//固定第i位为j
			if(i==1&&j==0) continue;
			p[i-1]=j;
			ans+=suff_free(sgn,L,i,L-i);
		}
		p[i-1]=s[i-1]-'0';
	}
	ans+=suff_free(sgn,L,L,0);//N本身
	return ans;
}
int main(){
	freopen("kpsum.in","r",stdin);
	freopen("kpsum.out","w",stdout);
	for(int i=1;i<=18;i++) pow10[i]=10*pow10[i-1];
	scanf("%s",s);
	L=strlen(s);
	printf("%lld\n",calc());
	return 0;
}