NOI及NOIP需要知道的与自己的心得(2)

2019-03-28 10:33

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 2 #include 3 using namespace std; 4 5 int main() 6 { 7 multiset ms; 8 ms.insert(10); 9 ms.insert(10); 10 ms.insert(13); 11 ms.insert(11); 12 ms.insert(13); 13 ms.insert(12); 14 ms.insert(13);

15 ms.erase(10);//删除键值为10的元素 16 multiset::iterator i;

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::iterator ii=ms.find(v); if(ii!=ms.end()) cout<<*ii<

//查找所有相同元素 v=11;

pair::iterator,multiset::iterator> p=ms.equal_range(v);

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 2 #include 3 using namespace std; 4 5 struct Person //定义结构体 6 { 7 int id; 8 char *name; 9 char *addr; 10 int age; 11 }; 12

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::iterator i;//定义游标i

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 2 #include 3 #define STACK_SIZE 100 4 using namespace std; 5 6 int main() 7 { 8 priority_queue q; 9 q.push(93); 10 q.push(5); 11 q.push(2); 12 q.push(33);

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


NOI及NOIP需要知道的与自己的心得(2).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:品质周例会管理制度1- 用于合并

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: