Markdown 与 KaTeX 测试
本文最后更新于 144 天前,其中的信息可能已经有所发展或是发生改变。

三维前缀和

s[x2][y2][z2]-s[x1-1][y2][z2]-s[x2][y1-1][z2]-s[x2][y2][z1-1]+s[x1-1][y1-1][z2]+s[x1-1][y2][z1-1]+s[x2][y1-1][z1-1]-s[x1-1][y1-1][z1-1]
for (int i = 1; i <= n; i++)
    for (int j = 1; j <= n; j++)
        for (int k = 1; k <= n; k++)
            b[i][j][k] += b[i][j][k - 1];
for (int i = 1; i <= n; i++)
    for (int k = 1; k <= n; k++)
        for (int j = 1; j <= n; j++)
            b[i][j][k] += b[i][j - 1][k];
for (int k = 1; k <= n; k++)
    for (int j = 1; j <= n; j++)
        for (int i = 1; i <= n; i++)
            b[i][j][k] += b[i - 1][j][k];

int i, j, k, x, y, z;
std::cin >> i >> x >> j >> y >> k >> z;
std::cout << b[x][y][z] + b[i - 1][j - 1][z] + b[i - 1][y][k - 1] + b[x][j - 1][k - 1]
    - b[x][y][k - 1] - b[x][j - 1][z] - b[i - 1][y][z] - b[i - 1][j - 1][k - 1] << '\n';

凸包直径 旋转卡壳

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5 + 5;

const double pi = acos(-1.0);
const double eps = 1e-9;

// 判断 x 的大小,<0 返回 -1,>0 返回 1,==0 返回 0
int sgn(double x) {
    if (fabs(x) < eps) return 0;
    else return x < 0 ? -1 : 1;
}

// 比较两个浮点数
int dcmp(double x, double y) {
    if (fabs(x - y) < eps) return 0;
    else return x < y ? -1 : 1;
}

struct Point {
    double x, y;
    Point() {}
    Point(double x, double y) : x(x), y(y) {}

    Point operator + (const Point& B) const { return Point(x + B.x, y + B.y); }
    Point operator - (const Point& B) const { return Point(x - B.x, y - B.y); }
    Point operator * (double k) const { return Point(x * k, y * k); }
    Point operator / (double k) const { return Point(x / k, y / k); }

    bool operator == (const Point& B) const {
        return sgn(x - B.x) == 0 && sgn(y - B.y) == 0;
    }
    bool operator<(Point B) { return sgn(x - B.x) < 0 || sgn(x - B.x) == 0 && sgn(y - B.y) < 0; }
    // 先按x再按y排序
};

typedef Point Vector;

class Geometry {
public:
    static double Distance(const Point& A, const Point& B) {
        return sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y));
    }

    static double Dot(const Vector& A, const Vector& B) {
        return A.x * B.x + A.y * B.y;
    }

    static int AngleJudge(const Vector& A, const Vector& B) {
        return sgn(Dot(A, B));
    }

    static double Len(const Vector& A) {
        return sqrt(Dot(A, A));
    }

    static double Len2(const Vector& A) {
        return Dot(A, A);
    }

    static double Angle(const Vector& A, const Vector& B) {
        return acos(Dot(A, B) / Len(A) / Len(B));
    }

    static double Cross(const Vector& A, const Vector& B) {
        return A.x * B.y - A.y * B.x;
    }

    static double Area2(const Point& A, const Point& B, const Point& C) {
        return Cross(B - A, C - A);
    }

    static double AreaTriangle(const Point& A, const Point& B, const Point& C) {
        return Area2(A, B, C) / 2;
    }

    static Vector Rotate(const Vector& A, double rad) {
        return Vector(A.x * cos(rad) - A.y * sin(rad), A.x * sin(rad) + A.y * cos(rad));
    }

    static Vector Normal(const Vector& A) {
        double len = Len(A);
        return Vector(-A.y / len, A.x / len);
    }

    static bool Parallel(const Vector& A, const Vector& B) {
        return sgn(Cross(A, B)) == 0;
    }
};

int n, m;
int sta[N], top;  // 将凸包上的节点编号存在栈里,第一个和最后一个节点编号相同
Point a[N], ch[N];

ll pf(ll x) { return x * x; }

ll dis(int p, int q) { return pf(ch[p].x - ch[q].x) + pf(ch[p].y - ch[q].y); }

ll sqr(int p, int q, int y) {
    return abs(Geometry::Cross((ch[q] - ch[p]), (ch[y] - ch[q])));
}

int Convex_hull(Point* p, int n, Point* ch) { // ch放凸包顶点,返回值是顶点个数
    n = unique(p, p + n) - p; // 去重
    sort(p, p + n);
    int v = 0;
    for (int i = 0; i < n; i++) {
        while (v > 1 && sgn(Geometry::Cross(ch[v - 1] - ch[v - 2], p[i] - ch[v - 1])) <= 0)
            v--;
        ch[v++] = p[i];
    }
    int j = v;
    // 求上凸包
    for (int i = n - 2; i >= 0; i--) {
        while (v > j && sgn(Geometry::Cross(ch[v - 1] - ch[v - 2], p[i] - ch[v - 1])) <= 0)
            v--;
        ch[v++] = p[i];
    }
    if (n > 1) v--;
    return v;
}

ll mx;

void get_longest() {  // 求凸包直径
    mx = 0;
    int j = 3;
    if (top < 3) {
        mx = dis(sta[1], sta[2]);
        return;
    }
    for (int i = 1; i <= top; ++i) {
        while (sqr(sta[i], sta[i + 1], sta[j]) <=
            sqr(sta[i], sta[i + 1], sta[j % top + 1]))
            j = j % top + 1;
        mx = max(mx, max(dis(sta[i + 1], sta[j]), dis(sta[i], sta[j])));
    }
}

void solve() {
    top = 0;
    cin >> n;
    for (int i = 0; i < n; i++) cin >> a[i].x >> a[i].y;
    int v = Convex_hull(a, n, ch); // 先求凸包
    for (int i = 1; i <= v; i++) sta[++top] = i - 1; // sta存储点的下标
    sta[v + 1] = 0; // 封闭图形,多放的是第一个点的下标
    get_longest(); // 求凸包直径,该题输出直径的平方
    cout << mx << '\n';
}
int main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int _T = 1;
    // cin >> _T;
    while (_T--)
        solve();
    return 0;
}

替罪羊树

写一种能够满足以下操作的数据结构:

  1. 插入一个数 $x$。
  2. 删除一个数 $x$(若有多个相同的数,应只删除一个)。
  3. 定义排名为比当前数小的数的个数 $+1$。查询 $x$ 的排名。
  4. 查询数据结构中排名为 $x$ 的数。
  5. 求 $x$ 的前驱(前驱定义为小于 $x$,且最大的数)。
  6. 求 $x$ 的后继(后继定义为大于 $x$,且最小的数)。

对于操作 3,5,6,不保证当前数据结构中存在数 $x$。

对于操作 $3,4,5,6$ 每行输出一个数,表示对应答案。

对于 $100%$ 的数据,$1\le n \le 10^5$,$|x| \le 10^7$

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
const double alpha = 0.75; // 不平衡率
struct Node {
    int ls, rs;
    int val;
    int tot; // 当前子树占用的空间数量,包括实际存储的节点和被标记删除的节点
    int size; // 子树上实际存储数字的数量
    bool del; // del=1 表示这个节点存有数字,=0 表示被删除
}t[N];
int order[N], cnt; // order[] 记录拍平后的结果,即那些存有数字的节点
int tree_stack[N], top = 0; // 用栈来回收和分配可用的节点
int root = 0; // 重建过程中根节点会变化
void inorder(int u) { // 中序遍历,“拍平”摧毁这棵子树
    if (!u) return;
    inorder(t[u].ls);
    if (t[u].del) order[++cnt] = u;
    else tree_stack[++top] = u;
    inorder(t[u].rs);
}
void initnode(int u) {
    t[u].ls = t[u].rs = 0;
    t[u].size = t[u].tot = t[u].del = 1;
}
void update(int u) {
    t[u].size = t[t[u].ls].size + t[t[u].rs].size + 1;
    t[u].tot = t[t[u].ls].tot + t[t[u].rs].tot + 1;
}
void build(int l, int r, int& u) {
    int mid = (l + r) >> 1;
    u = order[mid];
    if (l == r) { initnode(u); return; }
    if (l < mid) build(l, mid - 1, t[u].ls);
    if (l == mid) t[u].ls = 0;
    build(mid + 1, r, t[u].rs);
    update(u);
}
void rebuild(int& u) {
    cnt = 0;
    inorder(u);
    if (cnt) build(1, cnt, u);
    else u = 0;
}
bool notbalance(int u) {
    if ((double)t[u].size * alpha <= (double)max(t[t[u].ls].size, t[t[u].rs].size))
        return true;
    return false;
}
void Insert(int& u, int x) {
    if (!u) {
        u = tree_stack[top--];
        t[u].val = x;
        initnode(u);
        return;
    }
    t[u].size++;
    t[u].tot++;
    if (t[u].val >= x) Insert(t[u].ls, x);
    else Insert(t[u].rs, x);
    if (notbalance(u)) rebuild(u);
}
int Rank(int u, int x) {
    if (u == 0) return 0;
    if (x > t[u].val) return t[t[u].ls].size + t[u].del + Rank(t[u].rs, x);
    return Rank(t[u].ls, x);
}
int kth(int k) {
    int u = root;
    while (u) {
        if (t[u].del && t[t[u].ls].size + 1 == k) return t[u].val;
        else if (t[t[u].ls].size >= k) u = t[u].ls;
        else {
            k -= t[t[u].ls].size + t[u].del;
            u = t[u].rs;
        }
    }
    return t[u].val;
}
void del_k(int& u, int k) {
    t[u].size--;
    if (t[u].del && t[t[u].ls].size + 1 == k) {
        t[u].del = 0;
        return;
    }
    if (t[t[u].ls].size + t[u].del >= k) del_k(t[u].ls, k);
    else del_k(t[u].rs, k - t[t[u].ls].size - t[u].del);
}
void del(int x) {
    del_k(root, Rank(root, x) + 1);
    if (t[root].tot * alpha >= t[root].size)
        rebuild(root);
}
/*
// 测试:打印二叉树
void print_tree(int u) {
    if(u) {
        cout << "v = " << t[u].val << ", l = " << t[u].ls << ", r = " << t[u].rs << '\n';
        print_tree(t[u].ls);
        print_tree(t[u].rs);
    }
}
*/
int main()
{
    for (int i = N - 1; i >= 1; i--) tree_stack[++top] = i;
    int q; cin >> q;
    while (q--) {
        int op, x; cin >> op >> x;
        switch (op) {
        case 1: Insert(root, x); break;
        case 2: del(x); break;
        case 3: cout << Rank(root, x) + 1 << '\n'; break;
        case 4: cout << kth(x) << '\n'; break;
        case 5: cout << kth(Rank(root, x)) << '\n'; break;
        case 6: cout << kth(Rank(root, x + 1) + 1) << '\n'; break;
        }
        // cout << ">>" << '\n'; print_tree(root); << cout << '<<\n'; // 打印二叉树测试
    }
}

STL 代码:

#include <bits/stdc++.h>
using namespace std;
vector<int> v;
int main() {
    int q; cin >> q;
    while (q--) {
        int op, x; cin >> op >> x;
        if(op == 1) v.insert(lower_bound(v.begin(), v.end(), x), x);
        if(op == 2) v.erase(lower_bound(v.begin(), v.end(), x));
        if(op == 3) cout << lower_bound(v.begin(), v.end(), x) - v.begin() + 1 << '\n';
        if(op == 4) cout << v[x - 1] << '\n';
        if(op == 5) cout << v[lower_bound(v.begin(), v.end(), x) - v.begin() - 1] << '\n';
        if(op == 6) cout << v[upper_bound(v.begin(), v.end(), x) - v.begin()] << '\n';
    }
}

莫队

普通莫队

现有数列 $A_1,A_2,\ldots,A_N$,$Q$ 个询问 $(L_i,R_i)$,询问 $A_{L_i} ,A_{L_i+1},\ldots,A_{R_i}$ 是否互不相同。

第一行,两个整数$N,Q$。
第二行,$N$ 个整数$A_1, A_2, \ldots , A_N$。
接下来 $Q$ 行,每行两个整数 $L_i,R_i$。

对每个询问输出一行,YesNo

数据范围 $1 \le N,Q \le 10^5$,$1 \le A_i \le N$,$1 \le L_i \le R_i \le N$。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5 + 5;

int n, m;
int a[N], cnt[N], ans[N], tot;
struct Q {
    int l, r, id;
};
int pos[N];

void add(int x) {
    if (!cnt[a[x]]) tot++;
    cnt[a[x]]++;
}
void del(int x) {
    cnt[a[x]]--;
    if (!cnt[a[x]]) tot--;
}

void solve() {
    cin >> n >> m;
    int len = sqrt(n);
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        pos[i] = (i - 1) / len + 1;
    }
    vector<Q> v(m);
    for (int i = 0; i < m; i++) {
        cin >> v[i].l >> v[i].r;
        v[i].id = i;
    }
    sort(v.begin(), v.end(), [&](Q a, Q b) {
        if (pos[a.l] == pos[b.l]) {
            if (pos[a.l] & 1) return a.r > b.r;
            return a.r < b.r;
        }
        return pos[a.l] < pos[b.l];
        });
    int l = 1, r = 0;
    for (int i = 0; i < m; i++) {
        while (l > v[i].l) add(--l);
        while (r < v[i].r) add(++r);
        while (l < v[i].l) del(l++);
        while (r > v[i].r) del(r--);
        ans[v[i].id] = (tot == v[i].r - v[i].l + 1);
    }
    for (int i = 0; i < m; i++) {
        if (ans[i]) cout << "Yes\n";
        else cout << "No\n";
    }
}
int main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int _T = 1;
    // cin >> _T;
    while (_T--)
        solve();
    return 0;
}

带修改莫队

增加一个时间维度,分块长度 $len=n^{2/3}$

题目大意:给你一个序列,M 个操作,有两种操作:

  1. 修改序列上某一位的数字
  2. 询问区间 $[l,r]$ 中数字的种类数(多个相同的数字只算一个)
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e6 + 5;

int n, m;
struct Q { // 记录查询操作,t为这次查询之前经过的修改次数
    int id, t, l, r;
} q[N];
struct RE { // 记录修改操作
    int p, c;
} r[N];
int cnt[N];
int a[N], pos[N], ans[N], tot;
int qcnt, rcnt;

inline void add(int x) {
    if (cnt[x] == 0) tot++;
    cnt[x]++;
}
inline void del(int x) {
    if (cnt[x] == 1) tot--;
    cnt[x]--;
}

inline void solve() {
    cin >> n >> m;
    int len = pow(n, 2.0 / 3);
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        pos[i] = (i - 1) / len + 1;
    }
    for (int i = 0; i < m; i++) {
        char ch;
        int x, y;
        cin >> ch >> x >> y;
        if (ch == 'Q') {
            q[++qcnt] = { qcnt, rcnt, x, y };
        }
        else {
            r[++rcnt] = { x, y };
        }
    }
    sort(q + 1, q + 1 + qcnt, [&](Q a, Q b) {
        if (pos[a.l] != pos[b.l]) return pos[a.l] < pos[b.l];
        if (pos[a.r] != pos[b.r]) return pos[a.r] < pos[b.r]; 
        return a.t < b.t;
        });
    int L = 1, R = 0, last = 0; // last记录最后一次修改的时间点
    for (int i = 1; i <= qcnt; i++) {
        while (R < q[i].r) add(a[++R]);
        while (R > q[i].r) del(a[R--]);
        while (L > q[i].l) add(a[--L]);
        while (L < q[i].l) del(a[L++]);
        // 时间维度的变化
        while (last < q[i].t) { // 进行修改
            last++;
            if (L <= r[last].p && r[last].p <= R) {
                add(r[last].c); 
                del(a[r[last].p]);
            }
            swap(a[r[last].p], r[last].c); // 更新,回退时会用到
        }
        while (last > q[i].t) { // 回退修改
            if (L <= r[last].p && r[last].p <= R) {
                add(r[last].c);
                del(a[r[last].p]);
            }
            swap(a[r[last].p], r[last].c);
            last--; // 先修改再last--
        }
        ans[q[i].id] = tot;
    }

    for (int i = 1; i <= qcnt; i++) {
        cout << ans[i] << '\n';
    }
}
int main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int _T = 1;
    // cin >> _T;
    while (_T--)
        solve();
    return 0;
}

树上莫队

  • 给定 $n$ 个结点的树,每个结点有一种颜色。
  • $m$ 次询问,每次询问给出 $u,v$,回答 之$u,v$间的路径上的结点的不同颜色数。
  • $1 \leq n \leq 4*10^4,1 \leq m \leq 10^5$,颜色是不超过 $2×10^9$ 的非负整数。
#include <bits/stdc++.h>

const int N = 1e5 + 5;

int n, m;
int a[N], col[N], ans[N];
int tot, pos1[N], pos2[N], pos[N]; // pos1为某点第一次出现的位置(进入dfs序),pos2为第二次的位置(离开dfs序)
int cnt[N];
std::vector<int> ed[N];
bool vis[N];
int idx[N];

struct Q {
    int l, r, id, lca;
};

int fa[N][20], deep[N];
inline void DFS(int x, int f) {
    deep[x] = deep[f] + 1;
    fa[x][0] = f;
    for (int i = 1; (1 << i) <= deep[x]; i++)
        fa[x][i] = fa[fa[x][i - 1]][i - 1];
    for (int i : ed[x])
        if (i != f)
            DFS(i, x);
}
inline int lca(int x, int y) {
    if (deep[x] < deep[y]) std::swap(x, y);
    for (int i = 16; i >= 0; i--)
        if (deep[x] - (1 << i) >= deep[y])
            x = fa[x][i];
    if (x == y) return x;
    for (int i = 16; i >= 0; i--)
        if (fa[x][i] != fa[y][i])
            x = fa[x][i], y = fa[y][i];
    return fa[x][0];
}

inline void update(int u, bool op) {
    if (op)
        u = idx[u];
    if (!vis[u]) {
        cnt[a[u]]++;
        if (cnt[a[u]] == 1) tot++;
    }
    else {
        if (cnt[a[u]] == 1) tot--;
        cnt[a[u]]--;
    }
    vis[u] ^= 1;
}

signed main()
{
    std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);

    std::cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        std::cin >> a[i];
        col[i] = a[i]; // 用来排序
    }
    std::sort(col + 1, col + 1 + n);
    auto getid = [](int c) -> int {
        return std::lower_bound(col + 1, col + 1 + n, c) - (col + 1); // 对颜色进行离散化
        };
    for (int i = 1; i <= n; i++) {
        a[i] = getid(a[i]);
    }

    for (int i = 1; i < n; i++) {
        int u, v;
        std::cin >> u >> v;
        ed[u].push_back(v);
        ed[v].push_back(u);
    }

    std::vector<int> d;
    std::vector<bool> vis(n + 1, 0);
    auto dfs = [&](auto&& self, int u, int f) -> void {
        d.push_back(u);
        for (int v : ed[u]) {
            if (v == f || vis[v]) continue;
            vis[v] = 1;
            self(self, v, u);
        }
        d.push_back(u);
        };
    dfs(dfs, 1, 0); // 获取dfs序

    int len = sqrt(n << 1);
    for (int i = 0; i < (n << 1); i++) {
        if (!pos1[d[i]]) {
            pos1[d[i]] = i + 1;
        }
        else {
            pos2[d[i]] = i + 1;
        }
        pos[i + 1] = i / len + 1;
        idx[i + 1] = d[i];
    }
    DFS(1, 0); // lca初始化

    std::vector<Q> q(m);
    for (int i = 0; i < m; i++) {
        int u, v;
        std::cin >> u >> v;
        if (pos1[u] > pos1[v]) std::swap(u, v);
        int F = lca(u, v);
        if (F == u || F == v) {
            q[i].l = pos1[u];
            q[i].r = pos1[v];
            q[i].lca = 0;
        }
        else {
            q[i].l = pos2[u];
            q[i].r = pos1[v];
            q[i].lca = F; // dfs序中没有lca,要加上它带来的更新
        }
        q[i].id = i;
    }

    std::sort(q.begin(), q.end(), [&](Q a, Q b) {
        if (pos[a.l] == pos[b.l]) return a.r < b.r;
        return pos[a.l] < pos[b.l];
        });
    int l = 1, r = 0;
    for (int i = 0; i < m; i++) {
        while (l < q[i].l) update(l++, 1);
        while (l > q[i].l) update(--l, 1);
        while (r > q[i].r) update(r--, 1);
        while (r < q[i].r) update(++r, 1);
        if (q[i].lca) {
            update(q[i].lca, 0);
        }
        ans[q[i].id] = tot;
        if (q[i].lca) {
            update(q[i].lca, 0);
        }
    }
    for (int i = 0; i < m; i++) std::cout << ans[i] << std::endl;
}

扫描线

int lazy[N << 3];  // 标记了这条线段出现的次数
double s[N << 3];

struct node1 {
  double l, r;
  double sum;
} cl[N << 3];  // 线段树

struct node2 {
  double x, y1, y2;
  int flag;
} p[N << 3];  // 坐标

// 定义sort比较
bool cmp(node2 a, node2 b) { return a.x < b.x; }

// 上传
void pushup(int rt) {
  if (lazy[rt] > 0)
    cl[rt].sum = cl[rt].r - cl[rt].l;
  else
    cl[rt].sum = cl[rt * 2].sum + cl[rt * 2 + 1].sum;
}

// 建树
void build(int rt, int l, int r) {
  if (r - l > 1) {
    cl[rt].l = s[l];
    cl[rt].r = s[r];
    build(rt * 2, l, (l + r) / 2);
    build(rt * 2 + 1, (l + r) / 2, r);
    pushup(rt);
  } else {
    cl[rt].l = s[l];
    cl[rt].r = s[r];
    cl[rt].sum = 0;
  }
  return;
}

// 更新
void update(int rt, double y1, double y2, int flag) {
  if (cl[rt].l == y1 && cl[rt].r == y2) {
    lazy[rt] += flag;
    pushup(rt);
    return;
  } else {
    if (cl[rt * 2].r > y1) update(rt * 2, y1, min(cl[rt * 2].r, y2), flag);
    if (cl[rt * 2 + 1].l < y2)
      update(rt * 2 + 1, max(cl[rt * 2 + 1].l, y1), y2, flag);
    pushup(rt);
  }
}

void solve() {
    int temp = 1, n;
    double x1, y1, x2, y2, ans;
    cin >> n;
    ans = 0;
    for (int i = 0; i < n; i++) {
        scanf("%lf %lf %lf %lf", &x1, &y1, &x2, &y2);
        p[i].x = x1;
        p[i].y1 = y1;
        p[i].y2 = y2;
        p[i].flag = 1;
        p[i + n].x = x2;
        p[i + n].y1 = y1;
        p[i + n].y2 = y2;
        p[i + n].flag = -1;
        s[i + 1] = y1;
        s[i + n + 1] = y2;
    }
    sort(s + 1, s + (2 * n + 1));  // 离散化
    sort(p, p + 2 * n, cmp);  // 把矩形的边的横坐标从小到大排序
    build(1, 1, 2 * n);       // 建树
    memset(lazy, 0, sizeof(lazy));
    update(1, p[0].y1, p[0].y2, p[0].flag);
    for (int i = 1; i < 2 * n; i++) {
        ans += (p[i].x - p[i - 1].x) * cl[1].sum;
        update(1, p[i].y1, p[i].y2, p[i].flag);
    }
    cout << ans << '\n';
}

分层图

给定一个带权无向图,求 $s$ 到 $t$ 的最短路,其中可以令路径上最多 $k \leq 10$ 条道路花费为 $0$。

#include <bits/stdc++.h>

int main()
{
    std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);

    int n, m, k, s, t;
    std::cin >> n >> m >> k >> s >> t;
    std::vector<std::vector<std::pair<int, int>>> ed(n * (k + 1));
    for (int i = 0; i < m; i++) {
        int a, b, c;
        std::cin >> a >> b >> c;
        for (int j = 0; j <= k; j++) {
            int u = a + n * j, v = b + n * j;
            ed[u].push_back({v, c});
            ed[v].push_back({u, c});
            if (j < k) {
                ed[u].push_back({v + n, 0});
                ed[v].push_back({u + n, 0});
            }
        }
    }

    std::vector<bool> vis(n * (k + 1), false);
    std::vector<int> dis(n * (k + 1), 1e9);

    auto dijkstra = [&](int s) -> void {
        std::priority_queue<std::pair<int, int>, std::vector<std::pair<int, int>>, std::greater<std::pair<int, int>>> q;
        q.push({0, s});
        dis[s] = 0;
        while (q.size()) {
            int u = q.top().second;
            q.pop();
            if (vis[u]) continue;
            vis[u] = 1;
            for (auto e : ed[u]) {
                int v = e.first, w = e.second;
                if (dis[v] > dis[u] + w) {
                    dis[v] = dis[u] + w;
                    q.push({dis[v], v});
                }
            }
        }
        };
    dijkstra(s);

    int ans = 1e9;
    for (int i = 0; i <= k; i++) {
        ans = std::min(ans, dis[t + n * i]);
    }

    std::cout << ans << '\n';
}

线性判定排列逆序数的奇偶性

bool parity = n & 1;
for (int i : p) if (~i) {
    for (int j = i; ~j; ) {
        std::tie(j, p[j]) = std::tuple{p[j], -1};
    }
    parity ^= 1;
}

tuple

#include <tuple>
auto t = std::make_tuple(1, 2.5, "Hello");

// get 获取元素
auto t = std::make_tuple(1, 2.5, "Hello");
int i = std::get<0>(t); // 1
double d = std::get<1>(t); // 2.5
const char* s = std::get<2>(t); // "Hello"

// tie 解包获取元素 
int i;
double d;
const char* s;

auto t = std::make_tuple(1, 2.5, "Hello");
std::tie(i, d, s) = t;

// size 获取size
auto t = std::make_tuple(1, 2.5, "Hello");
constexpr size_t size = std::tuple_size<decltype(t)>::value; // 3

// tuple_element 获取某个元素的类型
auto t = std::make_tuple(1, 2.5, "Hello");
using FirstType = std::tuple_element<0, decltype(t)>::type; // int
using SecondType = std::tuple_element<1, decltype(t)>::type; // double

// tuple_cat 连接多个tuple
auto t1 = std::make_tuple(1, 2.5);
auto t2 = std::make_tuple("Hello", 'a');
auto t3 = std::tuple_cat(t1, t2); // t3 is std::tuple<int, double, const char*, char>

// apply 将tuple中的元素作为参数传递给一个函数
void print(int i, double d, const char* s) {
    std::cout << i << ", " << d << ", " << s << std::endl;
}

auto t = std::make_tuple(1, 2.5, "Hello");
std::apply(print, t); // 1, 2.5, Hello

// for_each + index_sequence 遍历并使用元素
template<typename Tuple, std::size_t... I>
void print_tuple(const Tuple& t, std::index_sequence<I...>) {
    ((std::cout << (I == 0 ? "" : ", ") << std::get<I>(t)), ...);
    std::cout << std::endl;
}

template<typename... Args>
void print_tuple(const std::tuple<Args...>& t) {
    print_tuple(t, std::index_sequence_for<Args...>{});
}

int main() {
    auto t = std::make_tuple(1, 2.5, "Hello");
    print_tuple(t); // 1, 2.5, Hello
    return 0;
}

预处理组合数

for (int i = 0; i <= 5000; i++) {
        for (int j = 0; j <= i; j++) {
            if (!j) C[i][j] = 1;
            else C[i][j] = C[i - 1][j] + C[i - 1][j - 1];
            C[i][j] %= mod;
        }
    }

防爆模乘

以 $O(1)$ 计算 $a \cdot b % p$ ,由于不取模,常数比 int128 更小。其中 $1 \leq a,b,p \leq 10^{18}$。

int mul(int a, int b, int m) {
    int r = a * b - m * (int)(1.L / m * a * b);
    return r - m * (r >= m) + m * (r < 0);
}

jiangly 取模运算+组合数板子

using i64 = long long;
template<class T>
constexpr T power(T a, i64 b) {
    T res {1};
    for (; b; b /= 2, a *= a) {
        if (b % 2) {
            res *= a;
        }
    }
    return res;
}

constexpr i64 mul(i64 a, i64 b, i64 p) {
    i64 res = a * b - i64(1.L * a * b / p) * p;
    res %= p;
    if (res < 0) {
        res += p;
    }
    return res;
}

template<i64 P>
struct MInt {
    i64 x;
    constexpr MInt() : x {0} {}
    constexpr MInt(i64 x) : x {norm(x % getMod())} {}

    static i64 Mod;
    constexpr static i64 getMod() {
        if (P > 0) {
            return P;
        } else {
            return Mod;
        }
    }
    constexpr static void setMod(i64 Mod_) {
        Mod = Mod_;
    }
    constexpr i64 norm(i64 x) const {
        if (x < 0) {
            x += getMod();
        }
        if (x >= getMod()) {
            x -= getMod();
        }
        return x;
    }
    constexpr i64 val() const {
        return x;
    }
    constexpr MInt operator-() const {
        MInt res;
        res.x = norm(getMod() - x);
        return res;
    }
    constexpr MInt inv() const {
        return power(*this, getMod() - 2);
    }
    constexpr MInt &operator*=(MInt rhs) & {
        if (getMod() < (1ULL << 31)) {
            x = x * rhs.x % int(getMod());
        } else {
            x = mul(x, rhs.x, getMod());
        }
        return *this;
    }
    constexpr MInt &operator+=(MInt rhs) & {
        x = norm(x + rhs.x);
        return *this;
    }
    constexpr MInt &operator-=(MInt rhs) & {
        x = norm(x - rhs.x);
        return *this;
    }
    constexpr MInt &operator/=(MInt rhs) & {
        return *this *= rhs.inv();
    }
    friend constexpr MInt operator*(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res *= rhs;
        return res;
    }
    friend constexpr MInt operator+(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res += rhs;
        return res;
    }
    friend constexpr MInt operator-(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res -= rhs;
        return res;
    }
    friend constexpr MInt operator/(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res /= rhs;
        return res;
    }
    friend constexpr std::istream &operator>>(std::istream &is, MInt &a) {
        i64 v;
        is >> v;
        a = MInt(v);
        return is;
    }
    friend constexpr std::ostream &operator<<(std::ostream &os, const MInt &a) {
        return os << a.val();
    }
    friend constexpr bool operator==(MInt lhs, MInt rhs) {
        return lhs.val() == rhs.val();
    }
    friend constexpr bool operator!=(MInt lhs, MInt rhs) {
        return lhs.val() != rhs.val();
    }
    friend constexpr bool operator<(MInt lhs, MInt rhs) {
        return lhs.val() < rhs.val();
    }
};

template<>
i64 MInt<0>::Mod = 998244353;

constexpr int P = 998244353;
using Z = MInt<P>;

struct Comb {
    int n;
    std::vector<Z> _fac;
    std::vector<Z> _invfac;
    std::vector<Z> _inv;

    Comb() : n{0}, _fac{1}, _invfac{1}, _inv{0} {}
    Comb(int n) : Comb() {
        init(n);
    }

    void init(int m) {
        m = std::min<i64>(m, Z::getMod() - 1);
        if (m <= n) return;
        _fac.resize(m + 1);
        _invfac.resize(m + 1);
        _inv.resize(m + 1);

        for (int i = n + 1; i <= m; i++) {
            _fac[i] = _fac[i - 1] * i;
        }
        _invfac[m] = _fac[m].inv();
        for (int i = m; i > n; i--) {
            _invfac[i - 1] = _invfac[i] * i;
            _inv[i] = _invfac[i] * _fac[i - 1];
        }
        n = m;
    }

    Z fac(int m) {
        if (m > n) init(2 * m);
        return _fac[m];
    }
    Z invfac(int m) {
        if (m > n) init(2 * m);
        return _invfac[m];
    }
    Z inv(int m) {
        if (m > n) init(2 * m);
        return _inv[m];
    }
    Z binom(int n, int m) {
        if (n < m || m < 0) return 0;
        return fac(n) * invfac(m) * invfac(n - m);
    }
} comb;
本文采用 CC BY-NC-SA 4.0 协议许可
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
下一篇