题目

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

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


【分析】

首先,看清楚了,题目是 \(\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. Sublime Text3 python自动补全问题——Sublime Text3安装Anaconda插件

    学习python的时候 在编辑器的选择上会有很多选择,我最终还是选择了sublime text3. 相对于其他编辑器,sublime text有以下特性: 插件多,类似GoSublime,Emmet信 ...

  2. 101-PHP二维数组的元素输出三,封装成函数

    <?php $arr=array(array(76,87,68), array(65,89,95), array(90,80,66), array(90,95,65),5,234,56,'Hel ...

  3. 3. SSH 服务器安装

    1.查看SSH是否安装(检查是否装了SSH包) 输入命令:rpm -qa | grep ssh 2.安装SSH 服务 yum install openssh-server 3.查看SSH服务是否正在运 ...

  4. HDU 4862 JUMP 最小费用最大流

    2014 多校的B题,由于我不怎么搞图论,当时碰到这个题目,我怎么想都没往网络流方面弄,不过网络流真的是个好东西,对于状态多变,无法用动规或者数据结构来很好表示的时候,非常有用 这个题目要求每个点一定 ...

  5. Python MySQL Select

    章节 Python MySQL 入门 Python MySQL 创建数据库 Python MySQL 创建表 Python MySQL 插入表 Python MySQL Select Python M ...

  6. P 1022 D进制的A+B

    转跳点 :

  7. js实现鼠标单击或者双击事件

    // timer为全局变量 getClickEmail1(_type) { clearTimeout(this.timer); if (_type == 1) { if (event.detail = ...

  8. python虚拟环境配置(上)

    前言 嘿,小伙伴们,晚上好呀,我们又见面了,今天又给带来的是什么呢,咱们今天就来说一下python的虚拟环境,可能有的小伙伴会疑惑,python的虚拟环境有什么用呢,我们来一一探讨一下 虚拟环境的作用 ...

  9. 安装yii2 框架遇到的问题

    1按要求安装好yii2时,访问yii2欢迎页面时,始终提示 CAssetManager.basePath “/assets” is invalid. Please make sure the dire ...

  10. Centos 7.4 DNS域名解析

    1.安装部署包 yum -y install bind bind-utils bind-chroot 2.启动服务并设置开机自启动 [root@localhost ~]# systemctl star ...