主要内容:
1.可持久化线段树
2.树状数组
3.倍增求LCA
4.树上差分
5.树链剖分:重链剖分、长链剖分
6.dfs和bfs基本应用
7.搜索的最优性、可行性、记忆化剪枝
8.迭代加深搜索
9.二进制搜索
10.折半搜索
(今天先介绍两种,因为其他八种本蒟蒻上课没听懂)
可持久化线段树
定义:
可持久化线段树又被称为主席树。可持久化是指更新的同
时保留了历史版本,可以获得所有的历史版本。本质上是多颗线
段树,不过这些线段树共同使用了一部分枝干。
模板题:
P3919 【模板】可持久化线段树 1(可持久化数组)
题目背景
UPDATE : 最后一个点时间空间已经放大
2021.9.18 增添一组 hack 数据 by @panyf
标题即题意
有了可持久化数组,便可以实现很多衍生的可持久化功能(例如:可持久化并查集)
题目描述
如题,你需要维护这样的一个长度为 NN 的数组,支持如下几种操作
-
在某个历史版本上修改某一个位置上的值
-
访问某个历史版本上的某一位置的值
此外,每进行一次操作(对于操作2,即为生成一个完全一样的版本,不作任何改动),就会生成一个新的版本。版本编号即为当前操作的编号(从1开始编号,版本0表示初始状态数组)
输入格式
输入的第一行包含两个正整数 N, MN,M, 分别表示数组的长度和操作的个数。
第二行包含NN个整数,依次为初始状态下数组各位的值(依次为 a_iai,1 \leq i \leq N1≤i≤N)。
接下来MM行每行包含3或4个整数,代表两种操作之一(ii为基于的历史版本号):
-
对于操作1,格式为v_i \ 1 \ {loc}_i \ {value}_ivi 1 loci valuei,即为在版本v_ivi的基础上,将 a_{{loc}_i}aloci 修改为 {value}_ivaluei
-
对于操作2,格式为v_i \ 2 \ {loc}_ivi 2 loci,即访问版本v_ivi中的 a_{{loc}_i}aloci的值,生成一样版本的对象应为vi
输出格式
输出包含若干行,依次为每个操作2的结果。
输入输出样例
5 10 59 46 14 87 41 0 2 1 0 1 1 14 0 1 1 57 0 1 1 88 4 2 4 0 2 5 0 2 4 4 2 1 2 2 2 1 1 5 91
#include<bits/stdc++.h> #define mid ((l+r)>>1) using namespace std; struct chairman { int rt[1000001],T[20000001],L[20000001],R[20000001]; int cnt; int build(int l,int r) { int root=++cnt; if(l==r) { scanf("%d",&T[root]); return root; } L[root]=build(l,mid); R[root]=build(mid+1,r); return root; } int update(int pre,int l,int r,int &x,int &c) { int root=++cnt; if(l==r) { T[root]=c; return root; } L[root]=L[pre]; R[root]=R[pre]; if(x<=mid)L[root]=update(L[pre],l,mid,x,c); else R[root]=update(R[pre],mid+1,r,x,c); return root; } void query(int pre,int l,int r,int& x) { if(l==r) { printf("%d\n",T[pre]); return; } if(x<=mid)query(L[pre],l,mid,x); else query(R[pre],mid+1,r,x); } }ss; int main() { ss.cnt=0; int n,m; scanf("%d%d",&n,&m); ss.build(1,n); int v,cd,x,y; ss.rt[0]=1; for(int i=1; i<=m; ++i) { scanf("%d%d%d",&v,&cd,&x); if(cd==1) { scanf("%d",&y); ss.rt[i]=ss.update(ss.rt[v],1,n,x,y); } if(cd==2) { ss.rt[i]=ss.rt[v]; ss.query(ss.rt[v],1,n,x); } } return 0; }
折半搜索
类似于二分查找.
模板题:
P4799 [CEOI2015 Day2] 世界冰球锦标赛
题目描述
译自 CEOI2015 Day2 T1「Ice Hockey World Championship」
今年的世界冰球锦标赛在捷克举行。Bobek 已经抵达布拉格,他不是任何团队的粉丝,也没有时间观念。他只是单纯的想去看几场比赛。如果他有足够的钱,他会去看所有的比赛。不幸的是,他的财产十分有限,他决定把所有财产都用来买门票。
给出 Bobek 的预算和每场比赛的票价,试求:如果总票价不超过预算,他有多少种观赛方案。如果存在以其中一种方案观看某场比赛而另一种方案不观看,则认为这两种方案不同。
输入格式
第一行,两个正整数 NN 和 M(1 \leq N \leq 40,1 \leq M \leq 10^{18})M(1≤N≤40,1≤M≤1018),表示比赛的个数和 Bobek 那家徒四壁的财产。
第二行,NN 个以空格分隔的正整数,均不超过 10^{16}1016,代表每场比赛门票的价格。
输出格式
输出一行,表示方案的个数。由于 NN 十分大,注意:答案 \le 2^{40}≤240。
输入输出样例
5 1000 100 1500 500 500 1000
8
#include<bits/stdc++.h> #define ll long long #define R register #define N 55 using namespace std; template<typename T>inline void read(T &a) { char c=getchar(); T x=0,f=1; while(!isdigit(c)) { if(c=='-')f=-1; c=getchar(); } while(isdigit(c)) { x=(x<<1)+(x<<3)+c-'0'; c=getchar(); } a=f*x; } ll n,m,w[N],mid,suma[1<<21],sumb[1<<21],cnta,cntb,ans; inline void dfs(R int l,R int r,R ll sum,R ll a[],R ll &cnt) { if(sum>m)return; if(l>r) { a[++cnt]=sum; return; } dfs(l+1,r,sum+w[l],a,cnt); dfs(l+1,r,sum,a,cnt); } int main() { read(n); read(m); for(R int i=1; i<=n; i++)read(w[i]); mid=n>>1; dfs(1,mid,0,suma,cnta); dfs(mid+1,n,0,sumb,cntb); sort(suma+1,suma+1+cnta); for(R int i=1; i<=cntb; i++) ans+=upper_bound(suma+1,suma+1+cnta,m-sumb[i])-suma-1; printf("%lld\n",ans); return 0; }
后续我学会以后会将落下的补上