# 高等数据结构

# 并差集

#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
class DisjointSet {
    public :
        //rank 记录树的高度
        vector<int> rank, p;
        DisjointSet() {};
        DisjointSet(int size) {
            rank.resize(size, 0);
            p.resize(size, 0);
            for (int i = 0; i < size; i++)
                makeSet(i);
        }
        void makeSet(int i) {
            p[i] = i;
            rank[i] = 0;
        }
        bool same(int x, int y) {
            return findSet(x) == findSet(y);
        }
        void unite(int x, int y) {
            link(findSet(x), findSet(y));
        }
        void link(int x, int y) {
            // 包含 x 的树的高度更高,则将 y 的树合并到 x 上
            if (rank[x] > rank[y])
                p[y] = x;
            else {
                p[x] = y;
                if (rank[x] == rank[y])
                    rank[y]++;
            }
        }
        int findSet(int x) {
            if (x != p[x])
                p[x] = findSet(p[x]);
            return p[x];
        }
};
int main()
{
    int n, a, b,t, q;
    scanf("%d %d", &n, &q);
    DisjointSet ds = DisjointSet(n);
    for (int i = 0; i < q; i++) {
        scanf("%d %d %d", &t, &a, &b);
        if (t == 0)
            ds.unite(a, b);
        else
            printf("%d\n", ds.same(a, b) ? 1 : 0);
    }
    return 0;
}

# kd 树

#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
class Node {
public :
    int location;
    int p, l, r;
    Node() {};
};
class Point {
public:
    int id, x, y;
    Point() {};
    Point(int id, int x, int y) :id(id), x(x), y(y) {};
    bool operator < (const Point &p) const {
        return id < p.id;
    }
    void print() {
        printf("%d\n", id);
    }
};
const int maxx = 1000010;
const int NIL = -1;
int n;
Point p[maxx];
Node T[maxx];
int np;
bool lessX(const Point &p1, const Point &p2) { return p1.x < p2.x; }
bool lessY(const Point &p1, const Point &p2) { return p1.y < p2.y; }
int makeKDTree(int l, int r, int depth) {
    if (l >= r)
        return NIL;
    int mid = (l + r) / 2;
    int t = np++;
    if (depth % 2 == 0)
        sort(p + l, p + r, lessX);
    else
        sort(p + l, p + r, lessY);
    T[t].location = mid;
    T[t].l = makeKDTree(l, mid, depth + 1);
    T[t].r = makeKDTree(mid + 1, r, depth + 1);
    return t;
}
void find(int v, int sx, int tx, int sy, int ty, int depth, vector<Point> &ans) {
    int x = p[T[v].location].x;
    int y = p[T[v].location].y;
    if (sx <= x && x <= tx && sy <= y && y <= ty)
        ans.push_back(p[T[v].location]);
    if (depth % 2 == 0) {
        if (T[v].l != NIL && sx <= x)
            find(T[v].l, sx, tx,sy, ty, depth + 1, ans);
        if (T[v].r != NIL && x <= tx)
            find(T[v].r, sx, tx, sy, ty, depth + 1, ans);
    }else {
        if (T[v].l != NIL && sy <= y)
            find(T[v].l, sx, tx, sy, ty, depth + 1, ans);
        if (T[v].r != NIL && y <= ty)
            find(T[v].r, sx, tx, sy, ty, depth + 1, ans);
    }
}
int main()
{
    int x, y;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d %d", &x, &y);
        p[i] = Point(i, x, y);
        T[i].l = T[i].r = T[i].p = NIL;
    }
    np = 0;
    int root = makeKDTree(0, n, 0);
    int q;
    int sx, tx, sy, ty;
    vector<Point> ans;
    scanf("%d", &q);
    for (int i = 0; i < q; i++) {
        scanf("%d %d %d %d", &sx, &tx, &sy, &ty);
        ans.clear();
        find(root, sx, tx, sy, ty, 0, ans);
        sort(ans.begin(), ans.end());
        for (int j = 0; j < ans.size(); j++)
            ans[j].print();
        printf("\n");
    }
    return 0;
}

# 图进阶

# 所有点对间最短路径

#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxx = 110;
const LL INF = (1LL << 32);
int n,q;
LL d[maxx][maxx];
void floyd() {
    for (int k = 0; k < n; k++) {
        for (int i = 0; i < n; i++) {
            if (d[i][k] == INF)
                continue;
            for (int j = 0; j < n; j++) {
                if (d[k][j] == INF)
                    continue;
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
            }
        }
    }
}
int main()
{
    int u, v, c;
    scanf("%d %d", &n, &q);
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++)
            d[i][j] = i == j ? 0 : INF;
    }
    // 输入权值
    for (int i = 0; i < q; i++) {
        scanf("%d %d %d", &u, &v, &c);
        d[u][v] = c;
    }
    floyd();
    // 判断是否存在负环
    bool negative = false;
    for (int i = 0; i < n; i++)
        if (d[i][i] < 0)
            negative = true;
    if (negative)
        printf("NEGATIVE CYCLE\n");
    else {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (d[i][j] == INF)
                    printf("INF");
                else
                    printf("%d", d[i][j]);
                printf("%c", j == n - 1 ? '\n' : ' ');
            }
        }
    }
    return 0;
}

# 拓扑排序

#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
#include <list>
#include <algorithm>
using namespace std;
const int maxx = 100010;
const int INF = 1 << 30;
vector<int>G[maxx];
list<int>out;
bool flag[maxx];
int n;
int indeg[maxx];
void bfs(int s) {
    queue<int> q;
    q.push(s);
    while (!q.empty()) {
        int u = q.front(); q.pop();
        out.push_back(u);
        for (int i = 0; i < G[u].size(); i++) {
            int v = G[u][i];
            indeg[v]--;
            if (indeg[v] == 0 && flag[v] == 0) {
                flag[v] = true;
                q.push(v);
            }
        }
    }
}
void tsort() {
    for (int i = 0; i < n; i++) {
        indeg[i] = 0;
        flag[i] = false;
    }
    for (int u = 0; u < n; u++) {
        for (int i = 0; i < G[u].size(); i++) {
            int v = G[u][i];
            indeg[v]++;
        }
    }
    for (int u = 0; u < n; u++) {
        if (indeg[u] == 0 && flag[u] == 0)
            bfs(u);
    }
    for (list<int>::iterator it = out.begin(); it != out.end(); it++)
        printf("%d\n", *it);
}
int main()
{
    int s, t, m;
    scanf("%d %d", &n, &m);
    for (int i = 0; i < m; i++) {
        scanf("%d %d", &s, &t);
        G[s].push_back(t);
    }
    tsort();
    return 0;
}

# 关节点

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int maxx = 100010;
int n, m, order = 0;
int low[maxx], dfn[maxx], father[maxx], son[maxx];
//father: 父结点 son: 子结点个数 
vector<int> cutpoint, edge[maxx];
vector< pair<int, int> > cutedge;
void tarjan(int u)
{
    dfn[u] = low[u] = ++order;
    bool flag = false;
    for (int i = 0; i<edge[u].size(); i++)
    {
        int v = edge[u][i];
        if (!dfn[v])
        {
            son[u]++;
            father[v] = u;
            tarjan(v);
            if (low[v] >= dfn[u]) flag = true;
            // 点 u 为割点 
            if (low[v]>dfn[u]) cutedge.push_back(make_pair(min(v, u), max(v, u)));
            // 边 v-u 为割边 
            low[u] = min(low[u], low[v]);
        }
        else if (v != father[u]) low[u] = min(low[u], dfn[v]);
    }
    // 根节点若有两棵或两棵以上的子树则该为割点
    // 非根节点若所有子树节点均没有指向 u 的祖先节点的回边则为割点
    if ((father[u] == 0 && son[u]>1) || (father[u] && flag)) cutpoint.push_back(u);
}
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i++)
    {
        int u, v;
        scanf("%d%d", &u, &v);
        edge[u+1].push_back(v+1), edge[v+1].push_back(u+1);
    }
    tarjan(1);
    sort(cutedge.begin(), cutedge.end());
    sort(cutpoint.begin(), cutpoint.end());
    int len = cutpoint.size();
    for (int i = 0; i< len; i++) 
        printf("%d\n", cutpoint[i]-1);
    return 0;
}

# 树的直径

#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
using namespace std;
const int maxx = 100010;
const int INF = 1 << 30;
class Edge {
public:
    int t, w;
    Edge() {};
    Edge(int t ,int w):t(t), w(w) {};
};
vector<Edge> G[maxx];
int n, d[maxx];
bool vis[maxx];
int cnt;
void bfs(int s) {
    for (int i = 0; i < n; i++)
        d[i] = INF;
    queue<int> Q;
    Q.push(s);
    d[s] = 0;
    while (!Q.empty()) {
        int u = Q.front(); Q.pop();
        int len = G[u].size();
        for (int i = 0; i < len; i++) {
            Edge e = G[u][i];
            if (d[e.t] == INF) {
                d[e.t] = d[u] + e.w;
                Q.push(e.t);
            }
        }
    }
}
void solve() {
    // 从任选的结点 s 出发,选择距离 s 最远的结点 tgt
    bfs(0);
    int maxv = 0, tgt = 0;
    for (int i = 0; i < n; i++) {
        if (d[i] == INF)
            continue;
        if (maxv < d[i]) {
            maxv = d[i];
            tgt = i;
        }
    }
    // 从 tgt 出发,求结点 tgt 到最远结点的距离 maxv
    bfs(tgt);
    maxv = 0;
    for (int i = 0; i < n; i++) {
        if (d[i] == INF)
            continue;
        maxv = max(maxv, d[i]);
    }
    printf("%d\n", maxv);
}
int main() {
    int s, t, w;
    scanf("%d", &n);
    for (int i = 0; i < n - 1; i++) {
        scanf("%d %d %d", &s, &t, &w);
        // 无向图
        G[s].push_back(Edge(t, w));
        G[t].push_back(Edge(s, w));
    }
    solve();
    return 0;
}

# 最小生成树

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
const int maxx = 100010;
const int INF = 1 << 30;
class DisjointSet {
public:
    //rank 记录树的高度
    vector<int> rank, p;
    DisjointSet() {};
    DisjointSet(int size) {
        rank.resize(size, 0);
        p.resize(size, 0);
        for (int i = 0; i < size; i++)
            makeSet(i);
    }
    void makeSet(int i) {
        p[i] = i;
        rank[i] = 0;
    }
    bool same(int x, int y) {
        return findSet(x) == findSet(y);
    }
    void unite(int x, int y) {
        link(findSet(x), findSet(y));
    }
    void link(int x, int y) {
        // 包含 x 的树的高度更高,则将 y 的树合并到 x 上
        if (rank[x] > rank[y])
            p[y] = x;
        else {
            p[x] = y;
            if (rank[x] == rank[y])
                rank[y]++;
        }
    }
    int findSet(int x) {
        if (x != p[x])
            p[x] = findSet(p[x]);
        return p[x];
    }
};
class Edge {
public:
    int source, target, cost;
    Edge(int source = 0, int target = 0, int cost = 0) :
        source(source), target(target), cost(cost) {}
    bool operator < (const Edge &e) const {
        return cost < e.cost;
    }
};
int kruskal(int n, vector<Edge> edges) {
    int totalCost = 0;
    sort(edges.begin(), edges.end());
    DisjointSet dset = DisjointSet(n + 1);
    for (int i = 0; i < n; i++)
        dset.makeSet(i);
    int len = edges.size();
    for (int i = 0; i < len; i++) {
        Edge e = edges[i];
        // 判断是否会成环
        if (!dset.same(e.source, e.target)) {
            // 添加最小生成树的边
            //MST.push_back(e);
            totalCost += e.cost;
            dset.unite(e.source, e.target);
        }
    }
    return totalCost;
}
int main() {
    int n, m, cost;
    int source, target;
    scanf("%d %d", &n, &m);
    vector<Edge> edges;
    for (int i = 0; i < m; i++) {
        scanf("%d %d %d", &source, &target, &cost);
        edges.push_back(Edge(source, target, cost));
    }
    printf("%d\n", kruskal(n, edges));
    return 0;
}

# 几何学

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
#include <cmath>
#include <set>
using namespace std;
#define EPS  (1e-10)
#define equals(a,b) (fabs((a) - (b)) < EPS)
// 点类
class Point {
public :
    double x, y;
    Point() {};
    Point(double x, double y) :x(x), y(y) {}
    Point operator + (Point p) { return Point(x + p.x, y + p.y); }
    Point operator - (Point p) { return Point(x - p.x, y - p.y); }
    Point operator * (double a) { return Point(x * a, y * a); }
    Point operator / (double a) { return Point(x / a, y / a); }
    bool operator < (const Point &p) const {
        return x != p.x ? x < p.x : y < p.y;
    }
    bool operator == (const Point &p) const {
        return fabs(x - p.x) < EPS && fabs(y - p.y) < EPS;
    }
};
// 线段类
class Segment {
public:
    Point p1, p2;
    Segment() {};
    Segment(Point p1, Point p2) :p1(p1), p2(p2) {};
};
#define BOTTOM 0
#define LEFT 1
#define RIGHT 2
#define TOP 3
class EndPoint {
public :
    Point p;
    int seg, st; // 线段的 ID,端点的种类
    EndPoint() {}
    EndPoint(Point p, int seg, int st) :p(p), seg(seg), st(st) {}
    bool operator < (const EndPoint &ep) const {
        // 按 y 坐标升序排序
        if (p.y == ep.p.y) {
            return st < ep.st;
        }else {
            return p.y < ep.p.y;
        }
    }
};
EndPoint EP[2 * 1000010];
// 线段相交问题,曼哈顿几何
int manhattanIntersection(vector<Segment> S) {
    int n = S.size();
    // 按照端点的 y 坐标升序排序
    sort(EP, EP + (2 * n));
    set<int> BT;            // 二叉搜索树
    BT.insert(10000000001); // 设置标记
    int cnt = 0;
    for (int i = 0; i < 2 * n; i++) {
        if (EP[i].st == TOP)
            BT.erase(EP[i].p.x); // 删除上端点
        else if (EP[i].st == BOTTOM)
            BT.insert(EP[i].p.x);
        else if (EP[i].st == LEFT) {
            set<int>::iterator b = BT.lower_bound(S[EP[i].seg].p1.x);
            set<int>::iterator e = BT.upper_bound(S[EP[i].seg].p2.x);
            // 加上 b 到 e 距离
            cnt += distance(b, e);
        }
    }
    return cnt;
}
int main() {
    vector<Segment> S;
    Segment seg;
    int n , k = 0;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%lf %lf %lf %lf", &seg.p1.x, &seg.p1.y, &seg.p2.x, &seg.p2.y);
        // 调整端点 p1、p2,保证左小右大
        if (seg.p1.y == seg.p2.y) {
            if (seg.p1.x > seg.p2.x)
                swap(seg.p1, seg.p2);
        }
        else if (seg.p1.y > seg.p2.y) {
            swap(seg.p1, seg.p2);
        }
        // 将水平线段添加到端点列表
        if (seg.p1.y == seg.p2.y) {
            EP[k++] = EndPoint(seg.p1, i, LEFT);
            EP[k++] = EndPoint(seg.p2, i, RIGHT);
        }
        else { // 将垂直线段添加到端点列表
            EP[k++] = EndPoint(seg.p1, i, BOTTOM);
            EP[k++] = EndPoint(seg.p2, i, TOP);
        }
        S.push_back(seg);
    }
    printf("%d\n", manhattanIntersection(S));
    return 0;
}
#define EPS  (1e-10)
#define equals(a,b) (fabs((a) - (b)) < EPS)
// 点类
class Point {
public :
    double x, y;
    Point() {};
    Point(double x, double y) :x(x), y(y) {}
    Point operator + (Point p) { return Point(x + p.x, y + p.y); }
    Point operator - (Point p) { return Point(x - p.x, y - p.y); }
    Point operator * (double a) { return Point(x * a, y * a); }
    Point operator / (double a) { return Point(x / a, y / a); }
    bool operator < (const Point &p) const {
        return x != p.x ? x < p.x : y < p.y;
    }
    bool operator == (const Point &p) const {
        return fabs(x - p.x) < EPS && fabs(y - p.y) < EPS;
    }
};
// 线段类
class Segment {
public:
    Point p1, p2;
    Segment() {};
    Segment(Point p1, Point p2) :p1(p1), p2(p2) {};
};
// 圆类
class Circle {
public:
    Point c;
    double r;
    Circle() {};
    Circle(Point c, double r) :c(c), r(r) {}
};
// 定义向量
typedef Point Vector;
// 定义直线
typedef Segment Line;
// 定义多边形
typedef vector<Point> Polygon;
/*************************** 点、向量 ****************************/
double norm(Point p) { return p.x * p.x + p.y * p.y; }
double abs(Point p) { return sqrt(norm(p)); }
// 向量的内积
double dot(Point a, Point b) {
    return a.x * b.x + a.y * b.y;
}
// 向量的外积
double cross(Point a, Point b) {
    return a.x * b.y - a.y * b.x;
}
// 向量 a,b 是否正交 <==> 内积为 0
bool isOrthogonal(Vector a, Vector b) {
    return equals(dot(a, b), 0.0);
}
bool isOrthogonal(Point a1, Point a2, Point b1, Point b2) {
    return equals(dot(a1 - a2, b1 - b2), 0.0);
}
// 向量 a,b 是否平行 <==> 外积为 0
bool isParallel(Vector a, Vector b) {
    return equals(cross(a, b), 0.0);
}
bool isParallel(Point a1, Point a2, Point b1, Point b2) {
    return equals(cross(a1 - a2, b1 - b2), 0.0);
}
// 点 p 在线段 s 上的投影
Point project(Segment s, Point p) {
    Vector base = s.p2 - s.p1;
    double r = dot(p - s.p1, base) / norm(base);
    return s.p1 + base * r ;
}
// 以线段 s 为对称轴与点 p 成线对称的点
Point reflect(Segment s, Point p) {
    return p + (project(s, p) - p) * 2.0;
}
// 点 a 到点 b 的距离
double getDistance(Point a, Point b) {
    return abs(a - b);
}
// 线段 l 和点 p 的距离
double getDistanceLP(Line l, Point p) {
    return abs( cross(l.p2 - l.p1, p - l.p1) / abs(l.p2 - l.p1) );
}
// 线段 s 与点 p 的距离
double getDistanceSP(Segment s, Point p) {
    if (dot(s.p2 - s.p1, p - s.p1) < 0.0)
        return abs(p - s.p1);
    if (dot(s.p1 - s.p2, p - s.p2) < 0.0)
        return abs(p - s.p2);
    return getDistanceLP(s, p);
}
/************************* 线段 ********************************/
// 线段 s1,s2 是否正交 <==> 内积为 0
bool isOrthogonal(Segment s1, Segment s2) {
    return equals(dot(s1.p2 - s1.p1, s2.p2 - s2.p1), 0.0);
}
// 线段 s1,s2 是否平行 <==> 外积为 0
bool isParallel(Segment s1, Segment s2) {
    return equals(cross(s1.p2 - s1.p1, s2.p2 - s2.p1), 0.0);
}
// 逆时针方向 ccw(Counter-Clockwise)
static const int COUNTER_CLOCKWISE = 1;
static const int CLOCKWISE = -1;
static const int ONLINE_BACK = 2;
static const int ONLINE_FRONT = -2;
static const int ON_SEGMENT = 0;
int ccw(Point p0, Point p1, Point p2) {
    Vector a = p1 - p0;
    Vector b = p2 - p0;
    if (cross(a, b) > EPS) return COUNTER_CLOCKWISE;
    if (cross(a, b) < -EPS) return CLOCKWISE;
    if (dot(a, b) < -EPS) return ONLINE_BACK;
    if (norm(a) < norm(b)) return ONLINE_FRONT;
    return ON_SEGMENT;
}
// 判断线段 p1p2 和线段 p3p4 是否相交
bool intersect(Point p1, Point p2, Point p3, Point p4) {
    return (ccw(p1, p2, p3) * ccw(p1, p2, p4) <= 0 &&
        ccw(p3, p4, p1) * ccw(p3, p4, p2) <= 0);
}
// 判断线段 s1 和 s2 是否相交
bool intersect(Segment s1, Segment s2) {
    return intersect(s1.p1, s1.p2, s2.p1, s2.p2);
}
// 线段 s1 和线段 s2 的距离
double getDistance(Segment s1, Segment s2) {
    // 相交
    if (intersect(s1, s2))
        return 0.0;
    return min(min(getDistanceSP(s1, s2.p1), getDistanceSP(s1, s2.p2)),
        min(getDistanceSP(s2, s1.p1), getDistanceSP(s2, s1.p2)));
}
// 线段 s1 与线段 s2 的交点
Point getCrossPoint(Segment s1, Segment s2) {
    Vector base = s2.p2 - s2.p1;
    double d1 = abs(cross(base, s1.p1 - s2.p1));
    double d2 = abs(cross(base, s1.p2 - s2.p1));
    double t = d1 / (d1 + d2);
    return s1.p1 + (s1.p2 - s1.p1) * t;
}
/*************************** 圆 ****************************/
// 圆 c 和直线 l 的交点
pair<Point, Point> getCrossPoints(Circle c, Line l) {
    Vector pr = project(l, c.c);
    Vector e = (l.p2 - l.p1) / abs(l.p2 - l.p1);
    double base = sqrt(c.r * c.r - norm(pr - c.c));
    return make_pair(pr + e * base, pr - e * base);
}
// 圆 c1 和圆 c2 的交点
double arg(Vector p) { return atan2(p.y, p.x); }
Vector polar(double a, double r) { return Point(cos(r) * a, sin(r) * a); }
pair<Point, Point> getCrossPoints(Circle c1, Circle c2) {
    double d = abs(c1.c - c2.c);
    double a = acos((c1.r * c1.r + d * d - c2.r * c2.r) / (2 * c1.r * d));
    double t = arg(c2.c - c1.c);
    return make_pair(c1.c + polar(c1.r, t + a), c1.c + polar(c1.r, t - a));
}
/*************************** 多边形 ****************************/
// 点的内包
/*
    IN 2
    ON 1
    OUT 0
*/
int contains(Polygon g, Point p) {
    int n = g.size();
    bool x = false;
    for (int i = 0; i < n; i++) {
        Point a = g[i] - p, b = g[(i + 1) % n] - p;
        if (abs(cross(a, b)) < EPS && dot(a, b) < EPS) return 1;
        if (a.y > b.y) swap(a, b);
        if (a.y < EPS && EPS < b.y && cross(a, b) > EPS)
            x = !x;
    }
    return (x ? 2 : 0);
}
int cmp(Point A, Point B)                     // 竖直排序  
{
    return (A.y<B.y || (A.y == B.y&&A.x<B.x));
}
// 凸包
Polygon andrewScan(Polygon s) {
    Polygon u, l;
    int len = s.size();
    if (len < 3) return s;
    // 以 x,y 为基准升序排序
    sort(s.begin(), s.end());
    // 将 x 值最小的两个点添加到 u
    u.push_back(s[0]);
    u.push_back(s[1]);
    // 将 x 值最大的两个点添加到 l
    l.push_back(s[len - 1]);
    l.push_back(s[len - 2]);
    // 构建凸包上部
    for (int i = 2; i < len; i++) {
        for (int j = u.size(); j >= 2 && ccw(u[j - 2], u[j - 1], s[i]) >= 0; j--) {
            u.pop_back();
        }
        u.push_back(s[i]);
    }
    // 构建凸包下部
    for (int i = len - 3; i >= 0; i--) {
        for (int j = l.size(); j >= 2 && ccw(l[j - 2], l[j - 1], s[i]) >= 0; j--) {
                l.pop_back();
        }
        l.push_back(s[i]);
    }
    reverse(l.begin(), l.end());
    for (int i = u.size() - 2; i >= 1; i--)
        l.push_back(u[i]);
    return l;
}

# 动态规划

# 硬币问题

#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
const int INF = 1 << 30;
int main() {
    int n, m;
    int A[100];
    int T[50000 + 10];
    scanf("%d %d", &n, &m);
    for (int i = 0; i < m; i++)
        scanf("%d", &A[i]);
    for (int i = 0; i <= n; i++)
        T[i] = INF;
    T[0] = 0;
    for (int i = 0; i < m; i++) {
        for (int j = A[i]; j <= n; j++)
            T[j] = min(T[j], T[j - A[i]] + 1);
    }
    printf("%d\n", T[n]);
    return 0;
}

# 背包问题

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int INF = 1 << 30;
const int maxx = 10010;
int dp[maxx], s[maxx];
int w[maxx], v[maxx];
int main() {
    int n, W;
    int A[100];
    int T[50000 + 10];
    scanf("%d %d", &n, &W);
    memset(dp, 0, sizeof(dp));
    memset(s, 0, sizeof(s));
    for (int i = 1; i <= n; i++)
    {
        scanf("%d %d", &v[i], &w[i]);
        s[i] = s[i - 1] + w[i];
    }
    for (int i = 1; i <= n; i++)
    {
        int b = max(w[i], W - s[n] + s[i - 1]);
        for (int j = W; j >= b; j--)
            dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
    }
    printf("%d\n", dp[W]);
    return 0;
}

# 最长递增子序列

#include<iostream>  
#include<cmath>  
#include<cstdio>  
#include<cstring>  
#include<algorithm>  
using namespace std;
const int INF = 1e9;
int dp[110000];// 记录以 a [i] 结束的最长子序列的长度  
int a[110000];
int main()
{
    // freopen("in.txt","r",stdin);  
    int n;
    while (scanf("%d", &n) != EOF&&n)
    {
        for (int i = 0; i<n; i++)
            dp[i] = INF;
        for (int i = 0; i<n; i++)
            scanf("%d", &a[i]);
        int len = 0;
        for (int i = 0; i<n; i++)
        {
            *lower_bound(dp, dp + n, a[i]) = a[i];
        }
        printf("%d\n", lower_bound(dp, dp + n, INF) - dp);
    }
    return 0;
}

# 最大正方形

#include<iostream>  
#include<cmath>  
#include<cstdio>  
#include<cstring>  
#include<algorithm>  
using namespace std;
const int maxx = 1410;
int dp[maxx][maxx], G[maxx][maxx];
int getLargestSquare(int H, int W) {
    int maxWidth = 0;
    for (int i = 0; i < H; i++)
        for (int j = 0; j < W; j++) {
            dp[i][j] = (G[i][j] + 1) % 2;
            maxWidth |= dp[i][j];
        }
    for (int i = 1; i < H; i++)
        for (int j = 1; j < W; j++) {
            if (G[i][j]) {
                dp[i][j] = 0;
            }else {
                dp[i][j] = min(dp[i - 1][j - 1], 
                    min(dp[i][j - 1], dp[i - 1][j]) ) + 1;
                maxWidth = max(maxWidth, dp[i][j]);
            }
        }
    return maxWidth * maxWidth;
}
int main()
{
    int H, W;
    scanf("%d %d", &H, &W);
    for (int i = 0; i < H; i++)
        for (int j = 0; j < W; j++)
            scanf("%d", &G[i][j]);
    printf("%d\n", getLargestSquare(H, W));
    return 0;
}

# 最大长方形

#include<iostream>   
#include<cstdio>  
#include<algorithm>
#include<stack>
using namespace std;
const int maxx = 1410;
int H, W;
int buffer[maxx][maxx];
int T[maxx][maxx];
struct Rectangle { int height; int pos; };
int getLargestRectangle(int size, int height[]) {
    stack<Rectangle> S;
    int maxv = 0;
    // 使得栈顶元素必定大于最后元素,使得前面的值被计算进来
    height[size] = 0;
    for (int i = 0; i <= size; i++) {
        Rectangle rect;
        rect.height = height[i];
        rect.pos = i;
        // 当 S 为空
        if (S.empty())
            S.push(rect);
        // 栈顶的高度小于当前的高度
        else if (S.top().height < rect.height)
            S.push(rect);
        // 栈顶的高度大于当前的高度
        else if (S.top().height > rect.height) {
            int target = i;
            // 栈顶的高度大于等于当前的高度
            // 出现小于栈顶高度的代表 pre.pos~i 之间都存在高度为 pre.height 的干净瓷砖
            // 此时可以计算当前的面积
            while (!S.empty() && S.top().height >= rect.height) {
                Rectangle pre = S.top(); S.pop();
                // 长 * 宽
                int area = pre.height * (i - pre.pos);
                maxv = max(maxv, area);
                target = pre.pos;
            }
            // 加上前面的宽度(下标设置为之前的),以便后面有合适的瓷砖进行计算
            rect.pos = target;
            S.push(rect);
        }
    }
    return maxv;
}
int solve() {
    // 以行为直方图,计算纵向的干净长度
    for (int j = 0; j < W; j++) {
        for (int i = 0; i < H; i++) {
            if (buffer[i][j])
                T[i][j] = 0;
            else
                T[i][j] = (i > 0) ? T[i - 1][j] + 1 : 1;
        }
    }
    // 从行计算出当前行的最大面积
    int maxv = 0;
    for (int i = 0; i < H; i++)
        maxv = max(maxv, getLargestRectangle(W, T[i]));
    return maxv;
}
int main()
{
    scanf("%d %d", &H, &W);
    for (int i = 0; i < H; i++)
        for (int j = 0; j < W; j++)
            scanf("%d", &buffer[i][j]);
    printf("%d\n", solve());
    return 0;
}

# 数论

# 质数

#include<iostream>   
#include<cstdio>  
using namespace std;
int isPrime(int x) {
    if (x < 2)
        return 0;
    else if (x == 2)
        return 1;
    if (x % 2 == 0)
        return 0;
    for (int i = 3; i * i <= x; i += 2)
        if (x % i == 0)
            return 0;
    return 1;
}
int main()
{
    int n, t, cnt = 0;
    scanf("%d", &n);
    for (int i = 0; i < n; i++)
    {
        scanf("%d", &t);
        if (isPrime(t))
            cnt++;
    }
    printf("%d\n", cnt);
    return 0;
}

# 最大公约数

#include<iostream>   
#include<cstdio>  
using namespace std;
int gcd(int x, int y) {
    return y ? gcd(y, x % y): x;
}
int main()
{
    int x, y;
    scanf("%d %d", &x, &y);
    printf("%d\n", gcd(x, y));
    return 0;
}

# 幂乘

#include<iostream>   
#include<cstdio>  
using namespace std;
typedef long long LL;
LL mod_pow(LL n, LL m, LL mod) {
    LL res = 1;
    while (m > 0) {
        if (m & 1)
            res = res * n % mod;
        n = n * n % mod;
        m >>= 1;
    }
    return res;
}
int main()
{
    LL n, m;
    LL mod = 1000000007;
    scanf("%lld %lld", &n, &m);
    printf("%d\n", mod_pow(n, m , mod));
    return 0;
}

# 启发式搜索

# 八皇后问题

#include<iostream>   
#include<cstdio>  
#include<algorithm>
#include<stack>
using namespace std;
const int maxx = 8;
// 行列、斜列
int row[maxx], col[maxx];
int dpos[maxx * 2 - 1], dneg[maxx * 2 - 1];
int x[maxx][maxx];
void init() {
    for (int i = 0; i < maxx; i++)
        row[i] = col[i] = 0;
    for (int i = 0; i < maxx * 2 - 1; i++)
        dpos[i] = dneg[i] = 0;
    for (int i = 0; i < maxx; i++)
        for (int j = 0; j < maxx; j++)
            x[i][j] = 0;
}
void print() {
    for (int i = 0; i < maxx; i++) {
        for (int j = 0; j < maxx; j++) {
            if (x[i][j] && row[i] != j)
                return;
        }
    }
    for (int i = 0; i < maxx; i++) {
        for (int j = 0; j < maxx; j++) {
            printf("%c", row[i] == j ? 'Q' : '.');
        }
        printf("\n");
    }
}
void recursive(int i) {
    if (i == maxx) {
        print();
        return;
    }
    for (int j = 0; j < maxx; j++) {
        // 不适合放在这里
        if (col[j] || dpos[i + j] || dneg[i - j + maxx - 1])
            continue;
        // 尝试放下这里
        row[i] = j; col[j] = dpos[i + j] = dneg[i - j + maxx - 1] = 1;
        recursive(i + 1);
        // 恢复刚才的操作继续其他的操作
        row[i] = col[j] = dpos[i + j] = dneg[i - j + maxx - 1] = 0;
    }
}
int main()
{
    int k;
    int r, c;
    scanf("%d", &k);
    for (int i = 0; i < k; i++){
        scanf("%d %d", &r, &c);
        x[r][c] = 1;
    }
    recursive(0);
    return 0;
}

# 九宫格拼图

#include<iostream>   
#include<cstdio>  
#include<algorithm>
#include<queue>
#include<map>
#include<string>
using namespace std;
const int row = 3;
const int maxx = 9;
struct Puzzle {
    int f[maxx];
    int space;
    string path;
    bool operator < (const Puzzle &p) const {
        for (int i = 0; i < maxx; i++) {
            if (f[i] != p.f[i])
                return f[i] > p.f[i];
        }
        return false;
    }
};
const int dx[4] = { -1, 0, 1, 0 };
const int dy[4] = { 0, -1, 0 ,1 };
const char dir[4] = { 'u','l','d','r' };
bool isTarget(Puzzle p) {
    for (int i = 0; i < maxx; i++)
        if (p.f[i] != (i + 1))
            return false;
    return true;
}
string bfs(Puzzle s) {
    queue<Puzzle> Q;
    map<Puzzle, bool> V;
    Puzzle u, v;
    s.path = "";
    Q.push(s);
    V[s] = true;
    while (!Q.empty()) {
        u = Q.front(); Q.pop();
        if (isTarget(u))
            return u.path;
        int sx = u.space / row;
        int sy = u.space % row;
        for (int r = 0; r < 4; r++) {
            int tx = sx + dx[r];
            int ty = sy + dy[r];
            if (tx < 0 || ty < 0 || tx >= row || ty >= row)
                continue;
            v = u;
            swap(v.f[u.space], v.f[tx * row + ty]);
            v.space = tx * row + ty;
            if (!V[v]) {
                V[v] = true;
                v.path += dir[r];
                Q.push(v);
            }
        }
    }
    return "unsolveable";
}
int main()
{
    Puzzle in;
    for (int i = 0; i < maxx; i++) {
        cin >> in.f[i];
        if (in.f[i] == 0) {
            in.f[i] = maxx;
            in.space = i;
        }
    }
    string ans = bfs(in);
    printf("%d\n", ans.size());
    return 0;
}

# 十六格拼图

/*IDA * 算法 */
#include<iostream>   
#include<cstdio>  
#include<algorithm>
#include<queue>
#include<map>
#include<string>
using namespace std;
const int row = 4;
const int maxx = 16;
const int LIMIT = 100;
struct Puzzle {
    int f[maxx], space, MD;
};
const int dx[4] = { 0, -1, 0, 1 };
const int dy[4] = { 1, 0, -1 ,0 };
const char dir[4] = { 'r','u','l','d' };
int MDT[maxx][maxx];
Puzzle state;
int limit;
int path[LIMIT];
int getAllMD(Puzzle pz) {
    int sum = 0;
    for (int i = 0; i < maxx; i++) {
        if (pz.f[i] != maxx)
            sum += MDT[i][pz.f[i] - 1];
    }
    return sum;
}
bool isSolved() {
    for (int i = 0; i < maxx; i++)
        if (state.f[i] != i + 1)
            return false;
    return true;
}
bool dfs(int depth, int prev) {
    if (state.MD == 0)
        return true;
    // 如果当前的深度加上启发值超过了限制,则进行剪枝
    if (depth + state.MD > limit)
        return false;
    int sx = state.space / row;
    int sy = state.space % row;
    Puzzle tmp;
    for (int r = 0; r < 4; r++) {
        int tx = sx + dx[r];
        int ty = sy + dy[r];
        if (tx < 0 || ty < 0 || tx >= row || ty >= row)
            continue;
        if (max(prev, r) - min(prev, r) == 2)
            continue;
        tmp = state;
        // 计算曼哈顿距离的差值,同时交换拼图
        state.MD -= MDT[tx * row + ty][state.f[tx * row + ty] - 1];
        state.MD += MDT[sx * row + sy][state.f[tx * row + ty] - 1];
        swap(state.f[tx * row + ty], state.f[sx * row + sy]);
        state.space = tx * row + ty;
        if (dfs(depth + 1, r)) {
            path[depth] = r;
            return true;
        }
        state = tmp;
    }
    return false;
}
string iterative_deepening(Puzzle in) {
    // 初始状态的曼哈顿距离
    in.MD = getAllMD(in);
    for (limit = in.MD; limit <= LIMIT; limit++) {
        state = in;
        if (dfs(0, -100)) {
            string ans = "";
            for (int i = 0; i < limit; i++)
                ans += dir[path[i]];
            return ans;
        }
    }
    return "unsolvable";
}
int main()
{
    for (int i = 0; i < maxx; i++) {
        for (int j = 0; j < maxx; j++) {
            MDT[i][j] = abs(i / row - j / row) + abs(i % row - j % row);
        }
    }
    Puzzle in;
    for (int i = 0; i < maxx; i++) {
        cin >> in.f[i];
        if (in.f[i] == 0) {
            in.f[i] = maxx;
            in.space = i;
        }
    }
    string ans = iterative_deepening(in);
    printf("%d\n", ans.size());
    return 0;
}
/*A * 算法 */
#include<iostream>   
#include<cstdio>  
#include<algorithm>
#include<queue>
#include<map>
#include<string>
using namespace std;
const int row = 4;
const int maxx = 16;
const int LIMIT = 100;
struct Puzzle {
    int f[maxx], space, MD;
    int cost;
    bool operator < (const Puzzle &p) const {
        for (int i = 0; i < maxx; i++) {
            if (f[i] != p.f[i])
                return f[i] < p.f[i];
        }
        return false;
    }
};
struct State {
    Puzzle puzzle;
    int estimated;
    bool operator < (const State &s) const {
        return estimated > s.estimated;
    }
};
const int dx[4] = { 0, -1, 0, 1 };
const int dy[4] = { 1, 0, -1 ,0 };
const char dir[4] = { 'r','u','l','d' };
int MDT[maxx][maxx];
int getAllMD(Puzzle pz) {
    int sum = 0;
    for (int i = 0; i < maxx; i++) {
        if (pz.f[i] != maxx)
            sum += MDT[i][pz.f[i] - 1];
    }
    return sum;
}
int astar(Puzzle s) {
    priority_queue<State> PQ;
    s.MD = getAllMD(s);
    s.cost = 0;
    map<Puzzle, bool> V;
    Puzzle u, v;
    State initial;
    initial.puzzle = s;
    initial.estimated = getAllMD(s);
    PQ.push(initial);
    while (!PQ.empty()) {
        State st = PQ.top(); PQ.pop();
        u = st.puzzle;
        if (u.MD == 0)
            return u.cost;
        V[u] = true;
        int sx = u.space / row;
        int sy = u.space % row;
        for (int r = 0; r < 4; r++) {
            int tx = sx + dx[r];
            int ty = sy + dy[r];
            if (tx < 0 || ty < 0 || tx >= row || ty >= row)
                continue;
            v = u;
            // 计算曼哈顿距离的差值,同时交换拼图
            v.MD -= MDT[tx * row + ty][v.f[tx * row + ty] - 1];
            v.MD += MDT[sx * row + sy][v.f[tx * row + ty] - 1];
            swap(v.f[tx * row + ty], v.f[sx * row + sy]);
            v.space = tx * row + ty;
            if (!V[v]) {
                v.cost++;
                State news;
                news.puzzle = v;
                news.estimated = v.cost + v.MD;
                PQ.push(news);
            }
        }
    }
    return -1;
}
int main()
{
    for (int i = 0; i < maxx; i++) {
        for (int j = 0; j < maxx; j++) {
            MDT[i][j] = abs(i / row - j / row) + abs(i % row - j % row);
        }
    }
    Puzzle in;
    for (int i = 0; i < maxx; i++) {
        cin >> in.f[i];
        if (in.f[i] == 0) {
            in.f[i] = maxx;
            in.space = i;
        }
    }
    printf("%d\n", astar(in));
    return 0;
}