[???]求和(sum)
[题目]
【问题描述】
你的任务非常简单,计算1至n的k次方的和。
【输入格式】
输入文件不超过100行,第一行一个整数k。然后每行一个正整数n。
【输出格式】
对于每个询问输出一个正整数,即答案mod 99997后的结果。
【样例输入】
2
1
2
3
【样例输出】
1
5
14
【数据范围】
50%的数据N*K<=10^6
100%的数据N<=10^9,K<=100000
[报告]
这道题唯一没有弄懂的是,我自己写的一个秦九韶算法计算两个几乎要超范围的单精度整数乘法的时候为什么只能拿70分(是WA,非TLE),而转成LONG LONG再直接乘就AC了……
一拿到这道题,最直接的想法是推通项——但这个貌似是不可能的。所以就没思路了……
没思路怎么办?暴力!
既然是暴力,所以TLE是显然的,所以要尽量增加优化——比如对输入数据进行排序,就可以按顺序处理,一次遍历就可以搞定。另一个优化就是快速幂算法。加了这两个算法就70了(剩下的TLE)。
注意到a^k%m==(a+m)^k%m(这个不证明了,反正是正确的),那么其实只要记录下0^k,1^k,2^k,……,(m-1)^k(存入数组S)就可以了,然后后面的计算直接利用这些记录下的数就可以了(鄙人相信这个优化的有用性,不过加了还是70分……)。
还是TLE,怎么办呢?凉拌~
注意到每个A(就是题目中所说的N,下文中的N均为A的个数)远大于M(99997),于是S会被遍历很多次。那为什么不算出一个值B=Sum(S)?那么不需要一次又一次的遍历S了(悲剧的是,加了这个优化还是70……)
由于题目中求的是前N个数的K次幂和模M的值,根据求和定理,可以把S直接加起来,也就是S[i]表示前i个数的K次幂和模M的值。那S[M-1]就相当于上面的B,所以对于每个A,最后的答案就是(A/M*S[M-1]+S[A%M])%M。
所以该题目的时间复杂度为O(MlongK+N),O(MlongK)为初始化(计算S数组的复杂度),O(N)为最后直接输出答案的复杂度。空间复杂度为O(M)。
至此,该题目在算法上完美解决……
[程序]
// TASK: sum
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <cstdio>
#include <cstring>
#define N 100
#define M 99997
using namespace std;
ifstream fin ("sum.in");
ofstream fout ("sum.out");
long n,k;
long a[N+1];
long s[M+1];
long ans[N+1],p[N+1];
static inline long calc(long qs,long k)
{
if (k==0) return 1;
else if (k==1) return qs;
else
{
long long ret=calc(qs,k/2)%M;
ret=(ret*ret);
if (k%2) return (ret*qs)%M;
else return (ret%M);
}
}
int main(int argc, char *argv[])
{
fin >> k;
n=0;
memset(a,0,sizeof(a));
long mx=0;
for (;fin >> a[++n];)
if (a[n]>mx)
mx=a[n];
n--;
for (long i=M-1;i>=0;i--)
s[i]=calc(i,k);
for (long i=1;i<M;i++)
s[i]=(s[i-1]+s[i])%M;
for (long i=1;i<=n;i++)
fout << (((a[i]/M*s[M-1])+s[a[i]%M])%M) << endl;
return EXIT_SUCCESS;
}
【问题描述】
你的任务非常简单,计算1至n的k次方的和。
【输入格式】
输入文件不超过100行,第一行一个整数k。然后每行一个正整数n。
【输出格式】
对于每个询问输出一个正整数,即答案mod 99997后的结果。
【样例输入】
2
1
2
3
【样例输出】
1
5
14
【数据范围】
50%的数据N*K<=10^6
100%的数据N<=10^9,K<=100000
[报告]
这道题唯一没有弄懂的是,我自己写的一个秦九韶算法计算两个几乎要超范围的单精度整数乘法的时候为什么只能拿70分(是WA,非TLE),而转成LONG LONG再直接乘就AC了……
一拿到这道题,最直接的想法是推通项——但这个貌似是不可能的。所以就没思路了……
没思路怎么办?暴力!
既然是暴力,所以TLE是显然的,所以要尽量增加优化——比如对输入数据进行排序,就可以按顺序处理,一次遍历就可以搞定。另一个优化就是快速幂算法。加了这两个算法就70了(剩下的TLE)。
注意到a^k%m==(a+m)^k%m(这个不证明了,反正是正确的),那么其实只要记录下0^k,1^k,2^k,……,(m-1)^k(存入数组S)就可以了,然后后面的计算直接利用这些记录下的数就可以了(鄙人相信这个优化的有用性,不过加了还是70分……)。
还是TLE,怎么办呢?凉拌~
注意到每个A(就是题目中所说的N,下文中的N均为A的个数)远大于M(99997),于是S会被遍历很多次。那为什么不算出一个值B=Sum(S)?那么不需要一次又一次的遍历S了(悲剧的是,加了这个优化还是70……)
由于题目中求的是前N个数的K次幂和模M的值,根据求和定理,可以把S直接加起来,也就是S[i]表示前i个数的K次幂和模M的值。那S[M-1]就相当于上面的B,所以对于每个A,最后的答案就是(A/M*S[M-1]+S[A%M])%M。
所以该题目的时间复杂度为O(MlongK+N),O(MlongK)为初始化(计算S数组的复杂度),O(N)为最后直接输出答案的复杂度。空间复杂度为O(M)。
至此,该题目在算法上完美解决……
[程序]
// TASK: sum
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <cstdio>
#include <cstring>
#define N 100
#define M 99997
using namespace std;
ifstream fin ("sum.in");
ofstream fout ("sum.out");
long n,k;
long a[N+1];
long s[M+1];
long ans[N+1],p[N+1];
static inline long calc(long qs,long k)
{
if (k==0) return 1;
else if (k==1) return qs;
else
{
long long ret=calc(qs,k/2)%M;
ret=(ret*ret);
if (k%2) return (ret*qs)%M;
else return (ret%M);
}
}
int main(int argc, char *argv[])
{
fin >> k;
n=0;
memset(a,0,sizeof(a));
long mx=0;
for (;fin >> a[++n];)
if (a[n]>mx)
mx=a[n];
n--;
for (long i=M-1;i>=0;i--)
s[i]=calc(i,k);
for (long i=1;i<M;i++)
s[i]=(s[i-1]+s[i])%M;
for (long i=1;i<=n;i++)
fout << (((a[i]/M*s[M-1])+s[a[i]%M])%M) << endl;
return EXIT_SUCCESS;
}
为什么来到这,行将终结、匆忙纷扰、并且完全看不到救赎的世界。