主要内容:

1.可持久化线段树
2.树状数组
3.倍增求LCA
4.树上差分
5.树链剖分:重链剖分、长链剖分
6.dfs和bfs基本应用
7.搜索的最优性、可行性、记忆化剪枝
8.迭代加深搜索
9.二进制搜索
10.折半搜索

(今天先介绍两种,因为其他八种本蒟蒻上课没听懂)

可持久化线段树

定义:

  可持久化线段树又被称为主席树。可持久化是指更新的同
时保留了历史版本,可以获得所有的历史版本。本质上是多颗线
段树,不过这些线段树共同使用了一部分枝干。

模板题:

P3919 【模板】可持久化线段树 1(可持久化数组)

题目背景

UPDATE : 最后一个点时间空间已经放大

2021.9.18 增添一组 hack 数据 by @panyf

标题即题意

有了可持久化数组,便可以实现很多衍生的可持久化功能(例如:可持久化并查集)

题目描述

如题,你需要维护这样的一个长度为 NN 的数组,支持如下几种操作

  1. 在某个历史版本上修改某一个位置上的值

  2. 访问某个历史版本上的某一位置的值

此外,每进行一次操作(对于操作2,即为生成一个完全一样的版本,不作任何改动),就会生成一个新的版本。版本编号即为当前操作的编号(从1开始编号,版本0表示初始状态数组)

输入格式

输入的第一行包含两个正整数 N, MN,M, 分别表示数组的长度和操作的个数。

第二行包含NN个整数,依次为初始状态下数组各位的值(依次为 a_iai1 \leq i \leq N1iN)。

接下来MM行每行包含3或4个整数,代表两种操作之一(ii为基于的历史版本号):

  1. 对于操作1,格式为v_i \ 1 \ {loc}_i \ {value}_ivi 1 loci valuei,即为在版本v_ivi的基础上,将 a_{{loc}_i}aloci 修改为 {value}_ivaluei

  2. 对于操作2,格式为v_i \ 2 \ {loc}_ivi 2 loci,即访问版本v_ivi中的 a_{{loc}_i}aloci的值,生成一样版本的对象应为vi

输出格式

输出包含若干行,依次为每个操作2的结果。

输入输出样例

输入 #1
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(1N40,1M1018),表示比赛的个数和 Bobek 那家徒四壁的财产。

第二行,NN 个以空格分隔的正整数,均不超过 10^{16}1016,代表每场比赛门票的价格。

输出格式

输出一行,表示方案的个数。由于 NN 十分大,注意:答案 \le 2^{40}240。

输入输出样例

输入 #1
5 1000
100 1500 500 500 1000
输出 #1
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;
}

 后续我学会以后会将落下的补上