题目

发现题解都不够优雅,就自己来一篇

( 以下除【代码】处代码,其余均为现场手打,如有误请与本蒟蒻联系 )


【分析】

首先,看清楚了,题目是 \(\sum_{i=1}^ai^b\) 的余数 ,而不是 \(\sum_{i=1}^ab^i\) ( 等比数列求和了解一下 )

毕竟......本蒟蒻一开始就看错了......

好,进入正题,介于 \(a,b\leq 10^9\) ,暴力就想都不用想了,肯定过不了每一次乘法需要 \(O(b)\) 的时间,加法需要 \(O(a)\) 的时间,外部一个 \(O(N)\) 的循环,总复杂度 \(O(Nab)=10^{20}\) ,大概要 \(10^{12} s\) ,也就是大概 \(31710\) 年吧 逃

首先,有一个很优秀的方法可以优化乘法,那就是 快速幂

如果你要求 \(a^x\) 那么你就先求出 \(a^{\lfloor {x\over 2}\rfloor}\)

然后 \(a^{\lfloor {x\over 2}\rfloor}\times a^{\lfloor {x\over 2}\rfloor}\) 就完事啦!

递归边界在于当 \(x=1\) 时 \(a^1=a\)

那奇数怎么办?我们知道 \(a^4=a^2\times a^2\) ,但 \(a^5\neq a^2\times a^2\) 啊

没事,你想, \(a^5=a^2\times a^2\times a^1\) 对不对?

同理,\(a^x=a^{\lfloor {x\over 2}\rfloor}\times a^{\lfloor {x\over 2}\rfloor}\times a\) ( \(x\) 为奇数)

所以,我们可以递归地盘它了:

int pow(int a,int x){
if(x==1) return a;
int p=pow(a,x/2);
if(x%2==1) return (p*p%Mod)*a%Mod;
else return p*p%Mod;
}

当然,喜欢三目运算符和位运算的小伙伴们可以更优雅地盘它:

int pow(int a,int x){
if(x==1) return a;
int p=pow(a,x>>1);
return (p*p%Mod)*((x&1)?a:1)%Mod;
}

这边再推荐一下非递归的打法,因为有的地方的评测机可能会爆栈:

int pow(int a,int x){
int bas=a,ans=(x&1)?a:1;
while(x>>=1){
bas=bas*bas%Mod;
if(x&1) ans=ans*bas%Mod;
}
return ans;
}

好的,我们可喜的发现,由于每次递归都是对半,求 \(i^b\) 的方法优化到了 \(O(\log b)\),现在的总复杂度为 \(O(Na\log b)\) 了,大概为 \(10^{12}\) 大概要 \(10^4s\) 了,也就是 \(3\) 小时 再逃

这说明我们还是不够优雅的,我们还得继续优化


我们还能发现,根据同余的性质: \((a+10000)\equiv a (\mod 10000)\)

两边同时翻 \(b\) 次方得:

\((a+10000)^b\equiv a^b(\mod10000)\)

这说明了啥?说明我们对于任何大于 \(10000\) 的数 \(n\) ,它的 \(b\) 次方我们已经求过了,就是 \((n\%10000)^b\)

因为本人是 C++ 选手,所以习惯用 \(a\%b\) 表示 \(a\) 除以 \(b\) 的余数

好的,所以我们把 \(1\) ~ \(10000\) 的 \(b\) 次方存起来,比如用 \(c_i\) 表示 \(i^b\%10000\) 。

我们先预处理 \(c_1\)~\(c_{10000}\) ,接下来,每次我们要 \(i^b\) ,就直接用 \(c_{i\%10000}\) 即可。

耶!总复杂度又下降了,每次预处理都是 \(O(10000\log b)\) 的,统计是 \(O(a)\) 的

\(\because 10000\log b\leq 10^4\times 10^2=10^6\leq 10^9=a\)

\(\therefore O(10000\log b)+O(a)=O(a)\)

总复杂度为 \(O(Na)=10^{11}\) , 大概 \(20\) 分钟吧 继续逃


至此,我们还能发现,对于给定的 \(a\) ,我们需要将 \(c_1\)~\(c_{10000}\) 的加和计算 \(\lfloor{a\over 10000}\rfloor\) 次,剩下的就是从 \((\lfloor{a\over 10000}\rfloor\times 10000+1)\) 到 \(a\) 的再各多记一次

也就是把加和乘上 \(\lfloor{a\over 10000}\rfloor\) ,再加上 \(c_1\)~\(c_{a\%10000}\) 的和就可以了

有的机智的小朋友立即意识到了,可以把 \(c_1\)~\(c_{10000}\) 的和记录起来,接下来的一个循环搞掉,岂不美哉

但是为什么要那么复杂呢?

我们令 \(Add_n=\sum_{i=1}^nc_i\)

那么,我们的答案就应该是 \(Add_{10000}\times \lfloor{a\over 10000}\rfloor+Add_{a\%10000}\)

这个实际上叫做前缀和

不是吗?

所以现在很明确了吧,我们要的是 \(Add_n\) ,只要这个一出来,我们就可以根据公式,\(O(1)\) 输出答案了

那 \(Add_n\) 又怎么求? \(c_n\) 全部算出来然后每次算 \(Add_n\) 都一遍扫过去?

复杂度 \(O(n^2)\) 的事情过于暴力了吧

我们想:

\(Add_n=\sum_{i=1}^nc_i=\sum_{i=1}^{n-1}c_i+c_n=Add_{n-1}+c_n\)

所以我们只要一遍算 \(c_n\) 的时候统计 \(Add_n\) 即可

这次,预处理复杂度 \(O(10000\log b)\) ,输出答案是 \(O(1)\) 的,总复杂度 \(O(10000N\log b)=10^7\) 可以做到一秒出结果了


【进阶】

这边讲一个毫无意义的进阶,虽然对复杂度和答案毫无影响,但确实看起来更优雅:

由欧拉定理得: \(a^{\varphi(n)}\equiv1(\mod n)\)

对于右上角那个鬼东西,它叫欧拉函数,是指小于 \(n\) 的正整数中,与其互质的数的个数

也有的版本是说小于等于 \(n\) 的正整数中,与其互质的数的个数

关于欧拉函数的性质,你们可以看看我 这篇文章 中讲欧拉函数的那一部分,其它的可以跳过

至于欧拉定理,记住它就行 OI是不需要数学证明的

根据公式 \(\varphi(10000)=4000\) 所以,我们不需要算 \(b\) 次方的快速幂,\(b\%4000\) 的快速幂即可

这样,您的代码将看起来更加的优雅、大气

hrq:“我现在终于知道了,代码优雅指的就是别人看不懂!原来我代码不优雅是褒义词!”

(P.S. 虽然丝毫不影响复杂度)


【代码】

好的,废话了那么多,本蒟蒻要放 我码风极丑无比的 代码了

代码前面加了一些读入和输出优化,无视即可

#include<cstdio>
#include<cstring>
using namespace std;
#define f(a,b,c,d) for(register int a=b,c=d;a<=c;a++)
#define g(a,b,c,d) for(register int a=b,c=d;a>=c;a--)
#define File(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout);
typedef int i32;
typedef long long int i64;
const int Mod=10000;
namespace INPUT{
char s[1<<20|1],*p1=s,*p2=s;
inline char gc() { return (p1==p2)&&(p2=(p1=s)+fread(s,1,1<<20|1,stdin),p1==p2)?EOF:*(p1++); }
inline i32 read(){
register i32 ans=0;register char c=gc();register bool neg=0;
while(c<48||c>57) neg^=!(c^'-'),c=gc();
while(c>=48&&c<=57) ans=(ans<<3)+(ans<<1)+(c^48),c=gc();
return neg?-ans:ans;
}
}
namespace OUTPUT{
char s[1<<20|1],*cur=s;
inline void output(){ cur-=fwrite(s,1,cur-s,stdout); }
inline void print(i32 x){
char tmp[16],*p1=tmp,*p2=tmp;
p2+=sprintf(tmp,"%d",x);
if(cur-s+p2-p1>>20) output();
while(p1<=p2) *(cur++)=*(p1++);
cur--;
}
inline void print(char c){
if(cur-s+1>>20) output();
*(cur++)=c;
}
}
using namespace INPUT;
using namespace OUTPUT;
//前面全是读入输出优化,无视它们 inline i32 pow(i32 d_A,i32 d_X){
i32 d_Bas=d_A,d_Ans=(d_X&1)?d_A:1;
while(d_X>>=1){
d_Bas*=d_Bas;
d_Bas%=10000;
if(d_X&1) d_Ans*=d_Bas,d_Ans%=10000;
}
return d_Ans;
}//快速幂
inline i32 work(i32 d_A,i32 d_B){
i32 ar_d_Add[Mod+1]={0};
f(i,1,I,Mod) ar_d_Add[i]=ar_d_Add[i-1]+pow(i,d_B),ar_d_Add[i]-=( (ar_d_Add[i]>=Mod)?Mod:0 );
i32 d_Ans= d_A/Mod%Mod * ar_d_Add[Mod]%Mod + ar_d_Add[d_A%Mod];
return d_Ans>=Mod?(d_Ans-Mod):d_Ans;
}//计算结果
i32 main(){
i32 d_N=read();
while(d_N--){
int d_A=read(),d_B=read()%4000;
print( work(d_A,d_B) );
print('\n');
}
output();
return 0;
}//对于主函数尽可能剪短的特殊癖好

最后安利一下 本蒟蒻的博客

题解 P1630 【求和】的更多相关文章

  1. 洛谷——P1630 求和

    P1630 求和 题目描述 求1^b+2^b+……+a^b的和除以10000的余数. 输入输出格式 输入格式: 第一行包含一个正整数N,表示共有N组测试数据: 接下来N行,每行包含两个正整数a和b. ...

  2. luogu P1630 求和(枚举暴力)

    题意 题解 可以发现当a=10001时, 和1是等价的. 所以这题就水了. #include<iostream> #include<cstring> #include<c ...

  3. P1630 求和

    题意:求$\sum_{i=1}^a i^b,a,b\le 10^9$ 暴力只有30分QAQ(本数学蒟蒻当然想不到正解啦) 正解:模数很小,不难(?)想到$i^a%10000=(i+b)^a %1000 ...

  4. [51nod1236] 序列求和 V3(斐波那契数列)

    题面 传送门 题解 把求和的柿子用斐波那契数列的通项公式展开 \[ \begin{aligned} Ans &=\sum\limits_{i = 1}^{n} \left(\frac{(\fr ...

  5. CSP-S考前各种idea题解乱堆

    快要考试了我还是这么菜. 已经没有心思维护我的博客了.开一篇博文吧.可能会记得很乱. 这也许是我OI生涯的最后一篇博文了?? 肯定很长很长. 不可能的.谁知道什么时候我心态恢复就把上面两句话删掉开始在 ...

  6. Luogu题目集合[6/未完待续]

    1. P1327数列排序 题目描述 给定一个数列{an},这个数列满足ai≠aj(i≠j),现在要求你把这个数列从小到大排序,每次允许你交换其中任意一对数,请问最少需要几次交换? 输入输出格式 输入格 ...

  7. AtCoder Beginner Contest 088 (ABC)

    A - Infinite Coins 题目链接:https://abc088.contest.atcoder.jp/tasks/abc088_a Time limit : 2sec / Memory ...

  8. Poj3468 A Simple Problem with Integers (分块)

    题面 Poj 题解 区间求和\(+\)区间修改板子,这里用分块写的 #include <cmath> #include <cstdio> #include <cstrin ...

  9. 【noip】noip201503求和(题解可能不完美,但绝对详细)

    3. 求和 难度级别:B: 运行时间限制:1000ms: 运行空间限制:51200KB: 代码长度限制:2000000B 题目描述   一条狭长的纸带被均匀划分出了n个格子,格子编号从1到n.每个格子 ...

随机推荐

  1. jar包学习

    jar: java的压缩包,主要用于存储类文件,或者配置文件等. 命令格式: jar -cf 包名.jar 包目录 解压缩: jar -xvf 包名.jar 将jar包目录列表重定向到一个文件中: j ...

  2. 掌握这几点,轻松搞定Essay Cohesion写作

    Cohesion就是衔接,是留学生Essay写作中中一个很重要的评价标准.很多留学生在平时Essay写作中,主体段已经做到了有观点.有例子,字数也不差,但总是被评价为展开不够.说理不清.不好follo ...

  3. C# winform中ListView用法

    this.listView1.GridLines = true; //显示表格线 this.listView1.View = View.Details;//显示表格细节 this.listView1. ...

  4. ACM-Subset sum

    题目描述: Subset Sum Tags: 回溯 子集和问题的一个实例为〈 S,t 〉.其中,S={x1 ,x2 ,…, xn }是一个正整数的集合,c是一个正整数.子集和问题判定是否存在S的一个子 ...

  5. Web基础之Mybatis

    Web基础之Mybatis   对比JdbcTempalte,mybatis才能称得上是框架,JdbcTempalte顶多算是工具类,同时,对比Hibernate,Mybatis又有更多的灵活性,算是 ...

  6. Python开源库的bug

    scipy 在misc的pilutil.py中def fromimage(im, flatten=0)函数中, # workaround for crash in PIL, see #1613.im. ...

  7. 服务器搭建---Linux安装Node.js

    先去官网下载:https://nodejs.org/en/download/ 把压缩包上传到服务器的/usr/local/soft(博主习惯)文件夹下  解压文件: cd /usr/local/sof ...

  8. [Mathematics][MIT 18.03] Detailed Explanation of the Frequency Problems in Second-Order Differential Equation of Oscillation System

    Well, to begin with, I'd like to say thank you to MIT open courses twice. It's their generosity that ...

  9. HttpServlet中文乱码问题

    客户端提交数据给服务器端(Requset) 如果数据中带有中文的话,有可能会出现乱码情况,那么可以参照以下方法解决. 如果是GET方式 1.代码转码 String username = request ...

  10. 带你探索关于飞机Wi-Fi服务的神奇科学

    资料来源: Colin Anderson制片公司/ Getty图片社 在35000英尺的高空冲浪?哇哦,这当然是我们现在所期望的飞行方式了.根据2018年全球旅行者研究(2018 Global Tra ...