您的位置:首页 > 其它

【BZOJ1236】KPSUM,记数类问题(乱搞)

2016-03-28 15:13 357 查看
传送门

权限题,放一下题面

1236: SPOJ1433 KPSUM

Time Limit: 1 Sec Memory Limit: 162 MB

Submit: 163 Solved: 87

[Submit][Status][Discuss]

Description

给你一个正整数N,依次把1到N写下来,在每两个数字之间交替的加上+,-号(注意是数字不是数),求这个表达式的值。例如,N=15时,表达式为1-2+3-4+5-6+7-8+9-1+0-1+1-1+2-1+3-1+4-1+5

Input

一个正整数N。

Output

表达式的值。

Sample Input

15

Sample Output

14

数据范围:30%的数据中,N<=100。

100%的数据中,N<=10^15

写在前面:听Shallwe说,这是一篇论文的练习题= =

思路:(调了好久才A,为zky学长的号+AC尽一份力)

第一反应是打表找规律,但是发现这规律并不是那么直接明显,我们进一步分析发现,计算奇数位的数时,相邻的两个数和为1(-2+3,-1+0-0+1-0+1),所以可以直接算出来(即5,450,45000…)。为了方便,我们称形同(2,3)(100,101)的两个数为“数对”。计算偶数位时,很显然每一位前面的运算符是固定的,都是-..+..-..+..,个位、百位……前都是+,十位、千位……前都是-,所以我们可以类似递推得预处理一下,f[i][j]指第i位最大为j时的总和(直接点说,就是0-j999……9,j是第i位),但是都是偶数位的情况下,可能有点难理解,

举个例子说f[1][9]=0+1+2+3+4+5+6+7+8+9

f[2][1]=-1+0-1+1-1+2-1+3-1+4-1+5-1+6-1+7-1+8-1+9,

显然转移时就是

f[i][j]=f[i−1][9](j=0)

f[i][j]=f[i][j−1]+f[i−1][9]+j∗10i−1(i为奇数)

f[i][j]=f[i][j−1]+f[i−1][9]−j∗10i−1(i为偶数)

转移出来后就比较好处理了,这里我们分别以120和2456作为讨论

对120来说,我们可以分块计算0-9和10-99,0-9是奇数位数,直接ans+=5(显然,奇数位时答案直接加5,450,45000……),10-99为偶数位数,我们要ans+=f[2][9],但是f[2][9]里包含了f[2][0],即十位数最高为2的情况包含了十位为0的情况,但是我们的计算是从10开始的,所以要减去f[2][0]

对于100-120,它是奇数位数,所以利用之前得到的性质,计算100-120之间有多少个“数对”,显然这一段对答案的贡献为(120-1-100)/2-1+2-0(最后的120是孤立的,单独计算即可)

对2456来说,形同上计算0-9,10-99,100-999,最后计算1000-2456,这是偶数位数,我们可以继续把它分解成1000-1999(即f[4][1]-f[4][0]),2000-2399(即f[3][3]-2*400),2400-2449(即f[2][4]-2*50+4*50)最后计算时要在个位特判一下,普遍规律就是可以记录一下前边的数数字按位计算的和差×10的几次方×当前位数字(这里有些口胡,因为不是很(想+会)用式子表达)

注意:

还是补充一下个位特判好了,例如计算2400时,在十位的0是不计算的,但个位的0也是一次计算,所以要计算

代码:

#include<bits/stdc++.h>
#define LL long long
using namespace std;
LL x,k,n,ans;
LL p[20]={1},s[20]={0,5,0,450},f[20][10],wei[20];
LL fj(LL x)
{
LL ans=0;
for (int i=1;i<=20;i++)
{
if (!x) return ans;
if (i&1) ans+=x%10;
else ans-=x%10;
x/=10;
}
}
main()
{
scanf("%lld",&n);
for (int i=1;i<=17;i++) p[i]=p[i-1]*10;//p[i]=10^i
for (int i=5;i<=17;i+=2) s[i]=s[i-2]*100;//5,450,45000……
for (int i=1;i<=17;i++)
for (int j=0;j<=9;j++)
{
if (!j) f[i][j]=f[i-1][9];
else if (i&1) f[i][j]=f[i][j-1]+f[i-1][9]+p[i-1]*j;
else f[i][j]=f[i][j-1]+f[i-1][9]-p[i-1]*j;
}
for (int i=1;i<=16;i++)
{
if (p[i]>n) {k=i-1;break;}
if (i&1) ans=ans+s[i];
else ans=ans+f[i][9]-f[i][0];
}
if (k&1)
{
LL q=0,now=n;
for (int i=1;i<=k+1;i++)
wei[i]=now%10,now/=10;
ans-=f[k+1][0];
for (int i=k+1;i>=1;i--)
{
if (wei[i]||i==1) ans=ans+q*(wei[i]+(i==1))*p[i-1]+f[i][wei[i]+(i==1)-1];
if (i&1)q+=wei[i];
else q-=wei[i];
}
}
else
{
if (n&1)
ans+=(n-p[k])/2+1;
else ans=ans+(n-p[k]+1)/2-fj(n);
}
printf("%lld",ans);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: