快速幂
问题
输入,计算
。
基本算法
一个暴力的做法就是直接把个
乘起来,时间复杂度为
考虑一个递归的计算方法:

每次递归都会变为原先的一半,所以时间复杂度为
。
对于非递归的做法,考虑的二进制表示
。

对于每一项是前面一项的平方, 可以在
的时间内计算,然后再根据
的值, 决定每个
是否乘入答案中。
代码
递归的实现
long long pow(long long x, long long n, long long p) {
if (n == 0) {
return 1;
} else {
int t = pow(x * x % p, n / 2, p);
if (n % 2 == 1) {
t = t * x % p;
}
return t;
}
}
复制代码
非递归的实现
int pow(int x, int n, int p) {
int re = 1;
for (; n > 0; n >>= 1) {
if (n % 2 == 1) {
re = (long long)re * x % p;
}
x = (long long)x * x % p;
}
return re;
}
复制代码
- 一般使用非递归版本较多。
- 在数论和计数问题中,一般认为
.
为负数,会导致程序出错。
- 如果
,那么函数会返回
,而应该返回
,除了极少数坑人的题目,一般并不需要考虑这种情况。
- 如果
的范围超过了int,那么需要额外实现一个long long * long long % long long 的函数,见推广部分。
- x可能大于p,在一些情况中,这会导致第一次
x * x % p
越界。 - 如果不考虑代码风格,可以把
(n % 2 == 1)
替换为(n & 1)
。
其他语言
Python语言中的pow函数,可以直接计算整数的快速幂,非常适合用来做手速题。(其实你只需要在自己的模板中实现出快速幂即可)
推广
在一些情况中快速幂的可能非常大。
- 如果
是输入的高精度数字(一般为十进制)那么并不需要进行每次模二,除以二的快速幂;可以用十进制快速幂。
- 如果是通过其他方式计算的,比如希望计算
pow(x, pow(y, n), p)
可以先通过找循环节的方式把指数部分取模pow(x, pow(y, n), p) == pow(x, pow(y, n, p - 1), p)
这个算法对于所有有结合律的运算均可以优化,其他常见的如下
- 整数
- 矩阵
- 多项式
- 排列/置换
题目
luogu P1226
快速幂模板题,但是需要考虑很多特殊情况。
bzoj 4475
答案是pow(2, k * n, p)
参考资料
Wikipedia Exponentiation by squaring
无旋式Treap
介绍
是一种基于旋转操作的平衡树。它给每个结点随机分配一个优先级,使得整棵树从权值上看是二叉查找树,从优先级上看是一个堆,来做到期望
的深度。
无旋式 使用了
随机化的思想,但用
和
操作代替了旋转,因此比旋转式
拥有了更强大的功能,比如提取区间和快速合并,分裂。
基本算法
首先做一些定义,若 表示某结点,则
表示
的左右孩子,
表示
的子树大小,
表示
的权值,
表示
的随机优先级(越小越优先,即从
上看
是一个小根堆)。
无旋式 的两个基本操作是
和
。
表示合并两个
:
,满足
中所有结点的
都比
中的小。返回值是合并后的
。
考虑如何实现 。 首先如果
有一个为空,那么返回另一个即可。 否则 若
,那么说明
是合并后的根,那么令
的左儿子不变,右儿子变成
,返回
; 否则
是合并后的根,那么令
的右儿子不变,左儿子变成
,返回
。
每次递归都有一个结点变成其某个儿子,所以 的复杂度显然就是
树高之和。
分两种,一种是按权值大小分,一种是按结点个数分。
先讲按权值大小分。 表示给定一个
,返回两个
,分别包含
中权值
的结点和权值
的结点。
考虑如何实现 。 首先如果
为空,那么直接返回 {空,空}即可。 否则 如果
的权值
,那么令
,将
的右儿子变成
,返回
即可; 否则
的权值
,那么类似的,令
,将
的左儿子变成
,返回
即可。
每次递归 都会变成其某个儿子,因此
的复杂度显然就是
的树高。
按结点个数分是类似的。 表示给定一个
,返回两个
,
中包含
中权值前
小的结点,
中包含其他结点。
的实现是类似的。 首先如果
为空(这时
一定为
),那么直接返回 {空,空}即可。 否则 如果
,那么令
,将
的左儿子变成
,返回
即可。 否则
,那么令
,将
的右儿子变成
,返回
即可。
复杂度同样也是 的树高。
有了 和
这两个基本操作,就可以实现很多功能。
比如如果要提取一段区间的结点,就可以通过两次 得到,操作完了再通过两次
变回去。
代码
模板题:https://www.luogu.org/problemnew/show/P3369
实现基本操作后其他操作都非常简单,好写。
#include<bits/stdc++.h>
using namespace std;
int rand_int()
{
return RAND_MAX<=32768?rand()*32768+rand():rand();
}
struct Node
{
Node *cl,*cr;
int sz,val,pri;
Node (int v)
{
cl=cr=0;
sz=1;
val=v;
pri=rand_int();
}
void push_up()
{
sz=(cl?cl->sz:0)+(cr?cr->sz:0)+1;
}
};
Node *merge(Node *a,Node *b)
{
if(!a)return b;
if(!b)return a;
if(a->pri<b->pri)
{
a->cr=merge(a->cr,b);
a->push_up();
return a;
}
else
{
b->cl=merge(a,b->cl);
b->push_up();
return b;
}
}
void split_v(Node *a,int v,Node *&l,Node *&r)
{
if(!a){l=r=0;return ;}
if(a->val<=v)
{
split_v(a->cr,v,l,r);
a->cr=l;
l=a;
}
else
{
split_v(a->cl,v,l,r);
a->cl=r;
r=a;
}
a->push_up();
}
void split_sz(Node *a,int k,Node *&l,Node *&r)
{
if(!a){l=r=0;return ;}
int sz_l=a->cl?a->cl->sz:0;
if(k<=sz_l)
{
split_sz(a->cl,k,l,r);
a->cl=r;
r=a;
}
else
{
split_sz(a->cr,k-sz_l-1,l,r);
a->cr=l;
l=a;
}
a->push_up();
}
Node *rt;
void insert(int v)
{
Node *now=new Node(v),*l,*r;
split_v(rt,v,l,r);
rt=merge(merge(l,now),r);
}
void erase(int v)
{
Node *l,*r;
split_v(rt,v-1,l,r);
Node *r1,*r2;
split_sz(r,1,r1,r2);
rt=merge(l,r2);
}
int rank_of_key(int v)
{
Node *l,*r;
split_v(rt,v-1,l,r);
int ans=(l?l->sz:0)+1;
rt=merge(l,r);
return ans;
}
int key_of_rank(int k)
{
Node *l,*r;
split_sz(rt,k-1,l,r);
Node *r1,*r2;
split_sz(r,1,r1,r2);
int ans=r1->val;
rt=merge(l,merge(r1,r2));
return ans;
}
int find_pre(int v)
{
Node *l,*r;
split_v(rt,v-1,l,r);
Node *l1,*l2;
split_sz(l,l->sz-1,l1,l2);
int ans=l2->val;
rt=merge(merge(l1,l2),r);
return ans;
}
int find_suf(int v)
{
Node *l,*r;
split_v(rt,v,l,r);
Node *r1,*r2;
split_sz(r,1,r1,r2);
int ans=r1->val;
rt=merge(l,merge(r1,r2));
return ans;
}
int main()
{
int n;
cin>>n;
while(n--)
{
int op,x;
scanf("%d%d",&op,&x);
if(op==1)insert(x); else
if(op==2)erase(x); else
if(op==3)printf("%d\n",rank_of_key(x)); else
if(op==4)printf("%d\n",key_of_rank(x)); else
if(op==5)printf("%d\n",find_pre(x)); else
printf("%d\n",find_suf(x));
}
}
复制代码
题目
的题基本上都可以用无旋式
做。
https://www.luogu.org/problemnew/show/P3391
https://www.luogu.org/problemnew/show/P1110
https://www.luogu.org/problemnew/show/P2596
https://www.luogu.org/problemnew/show/P2286
https://www.luogu.org/problemnew/show/P2042
参考资料
《可持久化数据结构研究》,陈立杰