6 Webb.S
快速幂可以用位运算这个强大的工具实现 b and 1 //也就是取b的二进制最末位 b shr 1 //就是去掉b的二进制最末位
有了这个强大的工具,快速幂就好实现了! var
a,b,n:int64;
function f(a,b,n:int64):int64; var
t,y:int64; begin
t:=1;y:=a; while b<>0 do begin
if (b and 1)=1 then t:=t*y mod n;
y:=y*y mod n; //这里用了一个很强大的技巧,y*y即求出了 a^(2^(i-1)) ←不知道这是什么的看原理 b:=b shr 1; end; exit(t); end; begin
read(a,b,n); // n是模 write(f(a,b,n)); end.
八、(STL)multiset多重集合容器
multiset多重集合容器与set集合容器类似,也是采用红黑树组织元素数据,只是该容器允许将重复的元素键值插入,而set不允许。其基本使用方法见下例:
1 #include
15 ms.erase(10);//删除键值为10的元素 16 multiset
17 for(i=ms.begin();i!=ms.end();i++)//打印全部元素
6
7 Webb.S
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
cout<<*i<<' '; cout< multiset //查找所有相同元素 v=11; pair cout<<\大于等于第一个元素的为 \ cout<<\大于11的第一个元素为:\ ms.erase(p.first,p.second);//删除迭代器区间[p.first,p.second]上的所有元素 for(i=ms.begin();i!=ms.end();i++)//打印全部元素 cout<<*i<<' '; cout< for(i=p.first;i!=p.second;i++) cout<<*i<<' '; cout< cout<<\等于13的个数:\ ms.clear();//清除全部元素 system(\ return 0; } 结构体也可以使用multiset容器,请看下例: 1 #include 13 struct PersonOrder//此为比较函数,按id号大小比较 14 { 15 bool operator()(const Person &p1,const Person &p2) 16 { 17 return p1.id 7 8 Webb.S 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 int main() { Person PersonArray[]={ {3,\张三\浙江\ {4,\李四\北京\ {5,\王二\上海\ {1,\高强\广州\ {2,\刘文\新疆\ {6,\赵强\福建\ }; //创建了multiset容器ms,容器内元素数据以id进行键值比较 multiset ms(PersonArray,PersonArray+6,PersonOrder()); Person temp={9,\ ms.insert(temp);//插入新元素 cout<<\容器是否为空:\ multiset for(i=ms.begin();i!=ms.end();i++)//顺序输出记录 cout<<(*i).id<<' '<<(*i).name<<' '<<(*i).addr<<' '<<(*i).age< cout<<\学生人数:\ ms.clear();//清除全部元素 getchar(); return 0; } 九、(STL)priority_queue优先队列容器 优先队列也是一种从一端入队,另一端出队的队,不同于一般队列的是,队列中最大的元素总是位于队首位置,因此,元素的出队并非按照先进先出的要求,将最先入队的元素出队,而是将当前队列的最大元素出队。 优先队列容器使用的堆排序算法,每次将最大值出列。时间复杂度为O(nlogn)。 参考程序: 1 #include 8 9 Webb.S 13 14 15 16 17 18 19 20 21 22 23 24 q.push(52); q.push(12); cout<<\元素个数为:\ while(!q.empty()) { cout< q.pop();//出队列要先判断是否为空 } system(\ return 0; } 十、(图论)网络流 15.1 网络流初步 Ford-Fulkerson算法 O(C(V+E)) DFS找到可行流。加反向边。继续DFS,共C次。 ----- Edmonds-Karp 最短增广路算法 O(VE2) BFS找到可行流。加反向边。继续BFS。 15.2 最大流 15.2.1 Dinic算法 Dinic 算法 O(≤V2E) BFS进行标层。DFS找到可行流。 返回到最上层的流路径中的向下流量为0的点。 做类似EK的操作。继续DFS。 当回到原点不能再DFS的时候,结束DFS。 再次进行BFS标层。当汇点不能标到层次的时候。结束算法。 15.2.2 Sap算法 Sap 算法 O(< DFS找到可行流。若一个点无法前进,则修改编号。 重新编为它的下面的最小值+1。(即他能到的最小的点) 当找到可行流后,也要增加反向边。 ----- Gap优化 将编号统计到一个数组。 若存在断层,即有的编号不存在。 退出求最大流程序。 ----- 15.2.3 有上下界的最大流 统计每点的最低入流量与最低出流量。 增加两个虚拟源点与汇点。 连接虚拟源点->点,流量为该点最低入流量。 连接点->虚拟汇点,流量为该点的最低出流量。 连接原始汇点->原始源点,流量为+∞。 将两点之间的流量改为最大流量-最小流量。 做虚拟源点->虚拟汇点的最大流。 对于与虚拟点连接的边不用反向边。 核对所有必要边的流量是否为0,若有>0的则无解。 当得到最大流以后,将与虚拟点连接的边去掉。 9 10 Webb.S 再去掉原始汇点->原始源点的边。 对于剩下的图中再求最大流,得到流值。 对于连接到汇点的点,流量改成前面求得的流量+流向汇点最低流。 把这些连接到汇点的点的流量相加,和就是所求最大流。 15.4.1 最短路增广费用流 将两点之间的费用转化成两点之间的距离。 通过SPFA找到一条可行的最短流。 增加反向边,寻找最短流时该边权值为负。 最后的费用为Σ(每次的距离*流量)。 十一、(DP)RMQ——ST RMQ(Range Minimum/Maximum Query)问题是求区间最值问题。 来看一下ST算法是怎么实现的(以最大值为例): 首先是预处理,用一个DP解决。设a是要求区间最值的数列,f表示从第i个数起连续2^j个数中的最大值。例如数列3 2 4 5 6 8 1 2 9 7 ,f[1,0]表示第1个数起,长度为2^0=1的最大值,其实就是3这个数。f[1,2]=5,f[1,3]=8,f[2,0]=2,f[2,1]=4……从这里可以看出f其实就等于a。这样,Dp的状态、初值都已经有了,剩下的就是状态转移方程。我们把f平均分成两段(因为f一定是偶数个数字),从i到i+2^(j-1)-1为一段,i+2^(j-1)到i+2^j-1为一段(长度都为2^(j-1))。用上例说明,当i=1,j=3时就是3,2,4,5 和 6,8,1,2这两段。f就是这两段的最大值中的最大值。于是我们得到了动规方程F[i,j]=max(F[I,j-2],F[i+2^(j-1),j-1]). F[i,0]=a[i](边界条件) 接下来是得出最值,也许你想不到计算出f有什么用处,一般毛想想计算max还是要O(logn),甚至O(n)。但有一个很好的办法,做到了O(1)。还是分开来。如在上例中我们要求区间[2,8]的最大值,就要把它分成[2,5]和[5,8]两个区间,因为这两个区间的最大值我们可以直接由f[2,2]和f[5,2]得到。扩展到一般情况,就是把区间[l,r]分成两个长度为2^n的区间(保证有f对应)。直接给出表达式: k:=[ln(r-l+1)/ln(2)]; ans:=max(F[l,k],F[r-2^k+1,k]); 这样就计算了从i开始,长度为2^k次的区间和从r-2^k+1开始长度为2^k的区间的最大值(表达式比较烦琐,细节问题如加1减1需要仔细考虑 部分核心代码: read(n, q); for i := 1 to n do read(d[i, 0]); for j := 1 to trunc(ln(n) / ln(2)) do for i := 1 to n - 1 shl j + 1 do d[i,j] := max(d[i,j-1], d[i+1 shl (j-1),j-1]); for i := 1 to q do begin read(a, b); k := trunc(ln(b - a + 1) / ln(2)); rmq := max(d[a, k], d[b - 1 shl k + 1, k]); writeln(rmq); end; 10