自己的板子

图论

2-SAT加边

1. x and y = false
	说明两者不能同时选
	(x,true)->(y,false),(y,true)->(x,false)
2. x or y = true
	说明两者不能同时不选
	(x,false)->(y,true),(y,false)->(x,true)
3. x xor y = true
	说明两者不能一样
	(x,true)->(y,false),(y,true)->(x,false)
	(x,false)->(y,true),(y,false)->(x,true)
4. x xor y = false
	说明两者要一样
	(x,true)->(y,true),(y,true)->(x,true)
	(x,false)->(y,false),(y,false)->(x,false)
5. x and y = true
	两者一定要为true
	(x,false)->(x,true),(y,false)->(y,true)
6. x or y = false
	两者一定为false
	(x,true)->(x,false),(y,true)->(y,false)

单源最短路 Dijkstra算法

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX = 1e5+10;
const int M = 5e5+10;
const int INF = 0x7fffffff;
int cnt;
int n, m, s;
int head[MAX], dis[MAX], vis[MAX];

struct Node {
    int dis;
    int pos;
    bool operator < (const Node &x)const {
        return x.dis < dis;
    }
    Node(int D, int P): dis(D), pos(P){}
};

struct edge {
    int nxt, to, dis;
}a[M];

void ADD(int u, int v, int d)
{
    cnt++;
    a[cnt].to = v;
    a[cnt].dis = d;
    a[cnt].nxt = head[u];
    head[u] = cnt;
}

void dijkstra()
{
    dis[s] = 0;
    priority_queue<Node> Q;
    Q.push(Node(0, s));
    while (!Q.empty()) {
        Node qq = Q.top();
        Q.pop();
        int x = qq.pos; int D = qq.dis;
        if(vis[x]) continue;
        vis[x] = 1;
        for (int i = head[x]; i; i = a[i].nxt) {
            int y = a[i].to;
            if (dis[y] > dis[x] + a[i].dis) {
                dis[y] = dis[x] + a[i].dis;
                p[y] = i;
                Q.push(Node(dis[y], a[i].to));
            }
        }
    }
}

int main()
{
    cin >> n >> m >> s;
    for (int i = 1; i <= n; i++) {dis[i] = INF;}
    for (int i = 0; i < m; i++) {
        int from, to, dis;
        cin >> from >> to >> dis;
        ADD(from, to, dis);
    }
    dijkstra();
    for (int i = 1; i <= n; i++) cout << dis[i] << " ";
    return 0;
}

次短路

如果求第k短路将dijkstra中的flag == 2该为flag == k
这个写法每个点只能到一次,多次见下方

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int MAX = 1e5+120;
struct Edge{int to, nxt;double d;}e[MAX];
int head[MAX], idx, x[MAX], y[MAX];
void add(int u, int v, double d) {e[++idx].to = v, e[idx].nxt = head[u], head[u] = idx, e[idx].d = d;}
db calc(int i, int j) {return sqrt((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i]- y[j]));}
struct Node {int u; db d, g; bool vis[205]; Node(){memset(vis, 0, sizeof (vis));} };
int inq[MAX];
db dis[MAX];
bool operator < (Node x, Node y) {return x.d + x.g > y.d + y.g;}
void spfa(int n)
{
    fill(dis + 1, dis + 1 + n, 1000000000);
    dis[n] = 0;
    queue<int> Q;
    Q.push(n);
    while (!Q.empty()) {
        int u = Q.front(); Q.pop();
        inq[u] = 0;
        for (int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;double d = e[i].d;
            if (dis[v] > dis[u] + d) {
                dis[v] = dis[u] + d;
                if (!inq[v]) {
                    inq[v] = 1;Q.push(v);
                }
            }
        }
    }
}

db dijkstra(int n)
{
    int flag = 0;
    priority_queue<Node> Q;
    Node st;st.g = dis[1], st.d = 0, st.u = 1;
    Q.push(st);
    while (!Q.empty()) {
        Node q = Q.top(); Q.pop();
        if (q.vis[q.u]) continue;
        q.vis[q.u] = 1;
        if (q.u == n) flag++;
        if (flag == 2) return q.d;
        int u = q.u;
        for (int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            Node nxt = q; nxt.u = v, nxt.g = dis[v], nxt.d = q.d + e[i].d;
            Q.push(nxt);
        }
    }
    return -1;
}

int main()
{
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> x[i] >> y[i];
    }
    for (int i = 0; i < m; i++) {
        int u, v;
        cin >> u >> v;
        add(u, v, calc(u, v));add(v, u, calc(u, v));
    }
    spfa(n);
    printf("%.2lf", dijkstra(n));
    return 0;
}

K短路

有向边,所以需要建一个反向的图求终点距离。。。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX = 2e5+120;
struct Edge{int to, nxt;int d;}e[MAX];
int head[MAX], rh[MAX], idx;
void add(int head[], int u, int v, int d) {e[++idx].to = v, e[idx].nxt = head[u], head[u] = idx, e[idx].d = d;}
struct Node {int u, d, g;bool operator < (const Node &x)const {return x.d + x.g < g + d;}};
int inq[MAX];
int dis[MAX];

int s, en, k;
void spfa()
{
    memset(dis, 0x3f, sizeof (dis));
    dis[en] = 0;
    queue<int> Q;
    Q.push(en);
    while (!Q.empty()) {
        int u = Q.front(); Q.pop();
        inq[u] = 0;
        for (int i = rh[u]; i; i = e[i].nxt) {
            int v = e[i].to;int d = e[i].d;
            if (dis[v] > dis[u] + d) {
                dis[v] = dis[u] + d;
                if (!inq[v]) {
                    inq[v] = 1;Q.push(v);
                }
            }
        }
    }
}
int cnt[MAX];
int dijkstra()
{
    priority_queue<Node> Q;
    Node st;st.g = dis[s], st.d = 0, st.u = s;
    Q.push(st);
    if (dis[s] == 0x3f3f3f3f) return -1;
    while (!Q.empty()) {
       Node q = Q.top(); Q.pop();
       int u = q.u, d = q.d;
       cnt[u]++;
       if (cnt[en] == k) return d;
       for (int i = head[u]; i; i = e[i].nxt) {
           int v = e[i].to, dd = e[i].d;
           if (cnt[v] < k) {Q.push({v, d + dd, dis[v]});}
       }
    }
    return -1;
}

int main()
{
    int n, m;
    cin >> n >> m;
   for (int i = 1; i <= m ;i++) {
        int u, v, d;
        cin >> u >> v >> d;
        add(head, u, v, d);
        add(rh, v, u, d);
    }
    cin >> s >> en >> k;
    if (s == en) k++;
    spfa();
    printf("%d", dijkstra());
    return 0;
}

最短路计数

#include<bits/stdc++.h>
using namespace std;
#define sf(x) scanf("%d", &x)
#define pf(x) printf("%d\n", x)
typedef long long ll;
const int MAX = 2e6+10;
const int MOD = 100003;
int n, m;

int head[MAX], idx;
struct Edge{
    int to, nxt;
}edges[MAX * 3];
void add(int u, int v) 
{
    edges[++idx].to = v, edges[idx].nxt = head[u], head[u] = idx;
    edges[++idx].to = u, edges[idx].nxt = head[v], head[v] = idx;
}
int dis[MAX], ans[MAX], vis[MAX];
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= m; i++) {
        int u, v;
        sf(u), sf(v);
        add(u, v);
    }
    queue<int>Q;
    Q.push(1);
    dis[1] = 1, ans[1] = 1, vis[1] = 1;
    while(!Q.empty()) {
        int x = Q.front(); Q.pop();
        for (int i = head[x]; i; i = edges[i].nxt) {
            int y = edges[i].to;
            if (!vis[y]){dis[y] = dis[x] + 1; vis[y] = 1; Q.push(y);}
            if (dis[y] == dis[x] + 1) {ans[y] = (ans[y] + ans[x]) % MOD;}
        }
    }
    for (int i = 1; i <= n; i++) pf(ans[i]);
    return 0;
}

johnson全源最短路

//https://www.luogu.com.cn/problem/P5905
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX = 1e5+10;
ll n, m, head[MAX], idx, vis[MAX], h[MAX], dis[MAX], num[MAX], inq[MAX];
struct Edge{ll to, nxt, d;}e[MAX];
void add(ll u, ll v, ll d) {e[++idx].to = v, e[idx].nxt = head[u], e[idx].d = d, head[u] = idx;}

bool spfa(ll st) 
{
    queue<ll>Q;
    //memset(h, 0x3f, sizeof (h));
    for (int i = 0; i <= n; i++) h[i] = 0x7fffffff;
    h[st] = 0;
    Q.push(st);
    inq[st] = 1;
    while (!Q.empty()) {
        ll u = Q.front(); Q.pop();
        inq[u] = 0;
        for (int i = head[u]; i; i = e[i].nxt) {
            ll v = e[i].to, d = e[i].d;
            if (h[v] > h[u] + d) {
                h[v] = h[u] + d;
                num[v]++;
                if (num[v] >= n + 1 ) return false;
                if (!inq[v]) {Q.push(v); inq[v] = 1;}
            }
        }
    }
    return true;
}
struct Node{ll u, d; bool operator < (const Node &x) const{return x.d < d;}};
void dijkstra(int st)
{
    for (int i = 1; i <= n; i++) dis[i] = 1000000000;
    memset(vis, 0, sizeof (vis));
    priority_queue<Node> Q;
    dis[st] = 0;
    Q.push({st, 0});
    while (!Q.empty()) {
        Node q = Q.top(); Q.pop();
        int u = q.u;
        if (vis[u])continue;
        vis[u] = 1;
        for(int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to, d = e[i].d;
            if(dis[v] > dis[u] + d) {
                dis[v] = dis[u] + d;
                Q.push({v, dis[v]});
            }
        }
    }
}

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= m; i++) {
        int u, v, d;
        cin >> u >> v >> d;
        add(u, v, d);
    }
    for (int i = 1; i <= n; i++) {add(0, i, 0);}
    if (!spfa(0)) {cout << -1 << endl;return 0;}
    for (int i = 1; i <= n; i++) {
        for (int j = head[i]; j; j = e[j].nxt) {
            e[j].d += h[i] - h[e[j].to];
        }
    }
    for (int i = 1; i <= n; i++) {
        dijkstra(i);
        ll ans = 0;
        for (int j = 1; j <= n; j++) {
            if (dis[j] == 1000000000) {ans += dis[j] * j;}
            else ans = ans + j * (dis[j] + h[j] - h[i]);
        }
        cout << ans << endl;
    }
    return 0;
}

含负权的单源最短路 Bellman-Ford算法

#include<bits/stdc++.h>
using namespace std;
const int INF = 0x7fffffff;
const int MAX = 2e5 +10;
const int M = 5e5+10;
int cnt;
int head[MAX], num[MAX], d[MAX], inq[M], p[MAX];
int n, m;
struct Edge{
    int nxt, to, dis;
}e[MAX];

void ADD(int u, int v, int d)
{
    e[++cnt].to = v;
    e[cnt].nxt = head[u];
    e[cnt].dis = d;
    head[u] = cnt;
}

bool Bellman_Ford(int s)
{
    queue<int>Q;
    memset(inq, 0, sizeof(inq));
    memset(num, 0, sizeof(num));
    Q.push(s);
    for(int i = 0; i <= n; i++ ) d[i] = INF;
    d[s] = 0;
    inq[s] = 1;
    while (!Q.empty()) {
        int u = Q.front(); Q.pop();
        inq[u] = 0;
        for (int i = head[u]; i; i = e[i].nxt) {
            Edge& a = e[i];
            if (d[u] < INF && d[a.to] > d[u] + a.dis) {
                d[a.to] = d[u] + a.dis;
                p[a.to] = i;
                num[a.to]++;
                if(num[a.to] >= n) return false;
                if (!inq[a.to]) {Q.push(a.to); inq[a.to] = 1; }
            }
        }
    }
    return true;
}

int main()
{
    int s;
    while (cin >> n >> m >> s) {
        if (n == 0) break;
        for (int i = 0; i < m; i++) {
            int from, to, dis;
            cin >> from >> to >> dis;
            ADD(from, to, dis);
        }
        if (!Bellman_Ford(s)){
            cout << "NO";
        }
        else {
            for (int i = 1; i <= n; i++) cout << d[i] << " ";
        }
        cout << endl;
    }
    return 0;
}

差分约束系统

///https://www.luogu.com.cn/problem/P5960
#include<bits/stdc++.h>
using namespace std;
const int MAX = 1e4+10;
int n, m;
int head[MAX], idx, dis[MAX], num[MAX], inq[MAX], vis[MAX];
struct Edge{int to, nxt, d;}edges[MAX];
void add(int u, int v, int d) {edges[++idx].to = v, edges[idx].nxt = head[u], edges[idx].d = d, head[u] = idx;}

bool spfa(int st)
{
    queue<int> Q;
    Q.push(st);
    memset(dis, -1, sizeof(dis));
    dis[st] = 0;
    inq[st] = 1;
    while (!Q.empty()) {
        int u = Q.front(); Q.pop();
        inq[u] = 0;
        for (int i = head[u]; i; i= edges[i].nxt) {
            int v = edges[i].to, d = edges[i].d;
            if (dis[v] < dis[u] + d) {
                dis[v] = dis[u] + d;
                num[v]++;
                if (num[v] >= n + 1) return true;
                if (!inq[v]) {Q.push(v);inq[v] = 1;}
            }
        }
    }
    return false;
}
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= m; i++) {
        int u, v, d;
        cin >> u >> v >> d;
        add(u, v, -d);
    }
    for (int i = 1; i<= n; i++) add(0, i, 0);
    if (spfa(0)) {cout << "NO" << endl;}
    else for (int i = 1; i <= n; i++) {cout << dis[i] << ' ';}cout << endl;
    return 0;
}

Bellman_Ford 判断负环存在

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX = 2e5+10;
const int INF = 0x3f3f3f3f;
int dis[MAX];

int head[MAX];
int cnt;
struct Edge {
    int to, nxt, dis;
}edges[MAX];
void add(int u, int v, int d)
{
    edges[++cnt].to = v;
    edges[cnt].nxt = head[u];
    edges[cnt].dis = d;
    head[u] = cnt;
}

int n, m;
int inf[MAX];
int num[MAX];

bool spfa(int u)
{
    queue<int> Q;
    for (int i = 1; i <= n; i++) Q.push(i);
    while(!Q.empty()) {
        int q = Q.front(); Q.pop();
        inf[q] = 0;
        for (int i = head[q]; i; i = edges[i].nxt) {
            Edge &e = edges[i];
            if (dis[q] + e.dis < dis[e.to]) {
                dis[e.to] = dis[q] + e.dis;
                num[e.to] = num[q] + 1;
                if (num[e.to] >= n) return false;
                if (!inf[e.to]) {Q.push(e.to); inf[e.to] = 1; }
            }
        }
    }
    return true;
}

int main()
{
    
    cin >> n >> m;
   
    for (int i = 0; i < m; i++) {
        int u, v, d;
        cin >> u >> v >> d;
        add(u, v, d);
    }
    if(!spfa(1)) cout << "Yes";
    else cout << "No";
    return 0;
}

Bellman_Ford 有边数限制的最短路

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX = 2e5+10;
const int INF = 0x3f3f3f3f;
struct Edge{
    int from, to, dis;
}edges[MAX];

int n, m, k;
int dis[MAX];
int backup[MAX];
bool Bellman_Ford()
{
    memset(dis, 0x3f, sizeof(dis));
    dis[1] = 0;
    for (int i = 0; i < k; i++) {
        memcpy(backup, dis, sizeof(dis));
        for (int j = 0 ; j < m; j++) {
            int f = edges[j].from, t = edges[j].to, d = edges[j].dis;
            dis[t] = min(dis[t], backup[f] + d);
        }
    }
    if (dis[n] > (INF / 2)) return false;
    else return true;
}

int main()
{
    cin >> n >> m >> k;
    for(int i = 0; i < m; i++) {
        int u, v, d;
        cin >> u >> v >> d;
        edges[i] = {u, v, d};
    }
    if(!Bellman_Ford()) cout << "impossible";
    else cout << dis[n];
    return 0;
}

无向图的最小环问题(路径输出)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF = 1e13;
ll d[1005][1005], ori[1005][1005], p[1005][1005];
vector<ll> G;
void dfs(ll i, ll j)
{
    ll k = p[i][j];
    if (!k) return;
    dfs(i, k);
    G.push_back(k);
    dfs(k, j);
}

void push(ll i, ll j, ll k)
{
    G.clear();
    G.push_back(k);
    G.push_back(i);
    dfs(i, j);
    G.push_back(j);
}

int main()
{
    ll n, m, s;
    cin >> n >> m;
    for (ll i = 1; i <= n; i++) {
        for (ll j = 1; j <= n; j++) {
            d[i][j] = ori[i][j] = INF;
        }
    }
    for (ll i = 1; i <= n; i++) d[i][i] = ori[i][i] = 0;
    for (ll i = 0; i < m; i++) {
        ll u, v, dd;
        cin >> u >> v >> dd;
        d[u][v] = d[v][u] = ori[u][v] = ori[v][u] = min(d[u][v], dd);
    }
    ll ans = INF;
    for (ll k = 1; k <= n; k++) {
        for (ll i = 1; i < k; i++) {
            for (ll j = i + 1; j < k; j++) {
                if (ans > d[i][j] + ori[i][k] + ori[k][j]) {
                    ans = d[i][j] + ori[i][k] + ori[k][j];
                    push(i, j, k);
                }
            }
        }

        for (ll i = 1; i <= n; i++) {
            for (ll j = 1; j <= n; j++) {
                if (d[i][j] > d[i][k] + d[k][j]) {
                    d[i][j] = d[i][k] + d[k][j];
                    p[i][j] = k;
                }
            }
        }
    }
    if (ans == INF) cout << "No solution.";
    else for (ll i = 0; i < G.size(); i++) cout << G[i] << ' ';
    return 0;
}

欧拉回路

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10, M = 4e5+10;
int head[N], idx = 1;
struct Edge{int to, nxt;}e[M];
void add(int u, int v) {e[++idx].to = v, e[idx].nxt = head[u], head[u] = idx;}
int used[M], ans[M >> 1], cnt, op, n, m, din[N], dout[N];
void dfs(int u)
{
    for (int &i = head[u]; i; i = e[i].nxt) {
        if (used[i]) {continue;}
        used[i] = 1;
        if (op == 1) {used[i ^ 1] = 1;}
        int t;
        if (op == 1) {
            t = i / 2;
            if (i % 2) t = -t;
        }else t = i - 1;
        int v = e[i].to;
        dfs(v);
        ans[cnt++] = t;  
    }
}

int main()
{
    cin >> op;
    cin >> n >> m;
    for (int i = 1; i <= m; i++) {
        int u, v;
        cin >> u >> v;
        add(u, v);
        if (op == 1) add(v, u);
        din[v]++, dout[u]++;
    }
    if (op == 1) {
        for (int i = 1; i <= n; i++) {
            if (dout[i] + din[i] & 1) {cout << "NO";return 0;}
        }
    }else {
        for (int i = 1; i <= n; i++) {
            if (din[i] != dout[i]) {cout << "NO"; return 0;}
        }
    }
    for (int i = 1; i <= n; i++) {
        if (head[i]) {dfs(i); break;}
    }
    if (cnt < m) {cout << "NO"; return 0;}
    cout << "YES" <<endl;
    for (int i = cnt - 1; i >= 0; i--) cout << ans[i] << ' ';
    return 0;
}

割点 并求点双连通分量

dfn为该点的dfs序,low为这个点能够回到的最小的dfs序,如果一个点能够从它的非父亲节点回到dfs序更小的点,则该点不为割点,桥同理

///https://www.luogu.com.cn/problem/P3388
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
ll head[505], idx;
struct Edge{ll to, nxt;}e[10010];
void add(ll u, ll v) {e[++idx].to = v, e[idx].nxt = head[u], head[u] = idx;}
ll dfn[505], low[505], dfscnt, stac[505], top, idcnt, cut[505];
vector<ll> dcc[505];
void tanjan(ll u, ll root)
{
    dfn[u] = low[u] = ++dfscnt;
    stac[++top] = u;
    ll child = 0;
    if (u == root && head[u] == 0) {
        idcnt++;
        dcc[idcnt].push_back(u);
        return;
    }
    for (ll i = head[u]; i; i = e[i].nxt) {
        ll v = e[i].to;
        if (!dfn[v]) {
            tanjan(v, root);
            low[u] = min(low[u], low[v]);
            if(dfn[u] <= low[v]) {
                child ++;
                if (u != root || child > 1) cut[u] = 1;
                ++idcnt;
                ll x;
                while (x = stac[top--]) {
                    dcc[idcnt].push_back(x);
                    if (x == v) break;
                }
                dcc[idcnt].push_back(u);
            }
        }else low[u] = min(low[u], dfn[v]);
    }
}

int main()
{
    ll tt = 1, n;
    while (cin >> n && n) {
        map<ll, ll>mp;
        ll m = 0;
        for (int i = 1; i <= idcnt; i++) dcc[i].clear();
        memset(dfn, 0, sizeof (dfn));memset(low, 0, sizeof (low)), dfscnt = 0, top = 0, idcnt = 0, memset(head, 0, sizeof (head)), idx = 0, memset(cut, 0, sizeof(cut));
        for (ll i = 1; i <= n; i++) {
            ll u, v;
            cin >> u >> v;
            m = max(m, max(u, v));
            add(u, v), add(v, u);
        }
        for (ll i = 1; i <= m; i++) {
            if (!dfn[i]) {
                tanjan(i, i);
            }
        }
        ll ans1 = 0, ans2 = 1;
        for (ll i = 1; i <= idcnt; i++) {
            ll Cut = 0;
            for (ll j = 0; j < dcc[i].size(); j++) {
                if (cut[dcc[i][j]]) Cut++;
            }
            if (Cut == 0) {
                if(dcc[i].size() > 1) ans1 += 2, ans2 = ans2 * (dcc[i].size() * (dcc[i].size() - 1) / 2);
                else ans1++;
            }else if (Cut == 1) {ans1++, ans2 = ans2 * (dcc[i].size() - 1);}
        }
        cout << "Case " << tt++ << ": " << ans1 << ' ' << ans2 << endl;
    }
    return 0;
}

如果边(u,v)代表从u到v有一条边,则如果v的low大于u的dfs序,则该边为桥

///https://www.luogu.com.cn/problem/UVA796
#include<bits/stdc++.h>
using namespace std;
#define PII pair<int,int>
const int MAX = 5e5+10;
int head[100005], idx, dfn[100005], low[100005], tim;
struct Edge{
    int to, nxt;
}edges[MAX << 1];
void add(int u, int v){edges[++idx].to = v, edges[idx].nxt = head[u], head[u] = idx;}

struct Cut {int u, v;}cut[MAX];int cut_num;
void tanjan(int u, int fa)
{
    dfn[u] = low[u] = ++tim;
    for(int i = head[u]; i; i = edges[i].nxt) {
        int v = edges[i].to;
        if (!dfn[v]) {
            tanjan(v, u);
            low[u] = min(low[u], low[v]);
            if (low[v] > dfn[u]) {
                cut[cut_num].u = min(u, v);cut[cut_num++].v = max(u, v);
            }
        }
        else if (v != fa)low[u] = min(low[u], dfn[v]);
    }
}
bool cmp(Cut a, Cut b) {if (a.u == b.u) return a.v < b.v; return a.u < b.u;}
int main()
{
    int t;
    while (cin >> t) {
        tim = 0;
        memset(dfn, 0, sizeof (dfn)); memset(low, 0, sizeof (low)); memset(head, 0, sizeof (head));
        idx = 0; cut_num = 0;
        for (int i = 0; i < t; i++) {
            int num; char a; char b; int u;
            cin >> u >> a >> num >> b;
            for (int j = 0; j < num; j++) {
                int v;
                cin >> v;
                v++;
                add(u + 1, v);
            }
        }
        for (int i = 1; i <= t; i++) {
            if(!dfn[i]) {
                tanjan(i, 0);
            }
        }
        sort(cut, cut + cut_num, cmp);
        cout << cut_num << " critical links" << endl;
        for (int i = 0; i < cut_num; i++) {
            cout << cut[i].u - 1 << " - " << cut[i].v - 1 << endl;
        }
        cout << endl;
    }
    return 0;
}

缩点

///https://www.luogu.com.cn/problem/P3387
#include<bits/stdc++.h>
using namespace std;
const int MAX = 1e5+10;
int n, m, tim;
int dfn[MAX], low[MAX];
struct Edge{
    int from, to, nxt;
}edges[MAX << 1], e[MAX << 1];
int head[MAX], idx, h[MAX];
int in[MAX], ans[MAX];
void add(int u, int v){edges[++idx].to = v, edges[idx].from = u, edges[idx].nxt = head[u], head[u] = idx;}
int len;
int d[MAX];
int stac[MAX], vis[MAX], cir[MAX];
void tanjan(int u)
{
    dfn[u] = low[u] = ++tim;
    stac[++len] = u; vis[u] = 1;
    for (int i = head[u]; i; i = edges[i].nxt) {
        int v = edges[i].to;
        if (!dfn[v]) {
            tanjan(v);
            low[u] = min(low[u], low[v]);
        } else if (vis[v]) {low[u] = min(low[u], low[v]);}
    }
    if (dfn[u] == low[u]) {
        int v;
        while (v = stac[len--]) {
            cir[v] = u;
            vis[v] = 0;
            if(u == v) break;
            d[u] += d[v];
        }
    }
}

int topo()
{
    queue<int> Q;
    for (int i = 1; i <= n; i++) {
        if (cir[i] == i && !in[i]) {Q.push(i); ans[i] = d[i];}
    }
    while (!Q.empty()) {
        int u = Q.front(); Q.pop();
        for (int i = h[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            ans[v] = max(ans[v], ans[u] + d[v]);
            in[v]--;
            if (!in[v]) Q.push(v);
        }
    }
    int a = 0;
    for (int i = 1; i <= n; i++) a = max(a, ans[i]);
    return a;
}

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> d[i];
    for(int i = 0; i < m; i++) {
        int u, v;
        cin >> u >> v;
        add(u, v);
    }
    for(int i = 1; i <= n; i++) {
        if(!dfn[i]) tanjan(i);
    }
    idx = 0;
    for(int i = 1; i <= m; i++) {
        int u = cir[edges[i].from], v = cir[edges[i].to];
        if (u == v) continue;
        e[++idx].to = v, e[idx].nxt = h[u], h[u] = idx;in[v]++;
    }
    cout << topo() << endl;
    return 0;
}

最大半(弱)连通子图

tanjan缩点后块序号就是topo序

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX = 2e6+10;
int head[100010], idx, rh[100010];
struct Edge{int to, nxt, from;}e[MAX];
void add(int u, int v, int head[]) {e[++idx].to = v, e[idx].from = u, e[idx].nxt = head[u], head[u] = idx;}
int dfn[100010], low[100010], dfscnt, vis[100010], stac[100010], top, id[100010], num[100010], idcnt;
int n, m, x;
int f[100010], g[100010];
void tanjan(int u)
{
    dfn[u] = low[u] = ++dfscnt;
    vis[u] = 1; stac[++top] = u;
    for (int i = head[u]; i; i = e[i].nxt) {
        int v = e[i].to;
        if (!dfn[v]) tanjan(v);
        if (vis[v]) low[u] = min(low[u], low[v]);
    }
    if (low[u] == dfn[u]) {
        int v;
        idcnt++;
        while (v = stac[top--]) {
            id[v] = idcnt;
            num[id[v]]++;
            vis[v] = 0;
            if (u == v) break;
        }
    }
}
unordered_map<ll, int> mp;

int main()
{
    cin >> n >> m >> x;
    for (int i = 1; i <= m; i++) {
        int u, v;
        cin >> u >> v;
        add(u, v, head);
    }
    for (int i = 1; i <= n; i++) {
        if (!dfn[i]) tanjan(i);
    }
    for (int i = 1; i <= m; i++) {
        int u = e[i].from, v = e[i].to;
        if (id[u] == id[v]) continue;
        if (mp[id[u]*1000010 + id[v]]) continue;
        mp[id[u]*1000010 + id[v]] = 1;
        add(id[u], id[v], rh);
    }
    for (int i = idcnt; i; i--) {
        if (!f[i]) {
            f[i] = num[i]; g[i] = 1;
        }
        for (int j = rh[i]; j; j = e[j].nxt) {
            int v = e[j].to;
            if (f[v] < f[i] + num[v]) {
                f[v] = f[i] + num[v];
                g[v] = g[i];
            }
            else if (f[v] == f[i] + num[v]) g[v] = (g[v] + g[i]) % x;
        }
    }
    int maxn = 0, sum = 0;
    for (int i = 1; i <= idcnt; i++) {
        if (f[i] > maxn) maxn = f[i], sum = g[i];
        else if (f[i] == maxn) sum = (sum + g[i]) % x;
    }
    cout << maxn << endl << sum << endl;
    return 0;
}

LCA倍增

#include<iostream>
#include<algorithm>
using namespace std;
#define MAX 500005

struct Tree{
    int now;
    int nxt;
}my[500010 << 1];
int head[MAX], flag, lg[MAX], depth[MAX], father[MAX][30];

void add(int a, int b)
{
   my[++flag].now = b;
   my[flag].nxt = head[a];
   head[a] = flag;
}

void BuildTree(int now, int fa)
{
    father[now][0] = fa;
    depth[now] = depth[fa] + 1;
    for (int i = 1; i <= lg[depth[now]]; i++) {
        father[now][i] = father[father[now][i - 1]][i - 1];
    }
    for (int i = head[now]; i; i = my[i].nxt) {
        if (my[i].now != fa) BuildTree(my[i].now, now);
    }
}

int LCA(int x, int y)
{
    if (depth[x] < depth[y]) swap(x, y);
    while (depth[x] > depth[y]) {x = father[x][lg[depth[x] - depth[y]] - 1];}
    if (x == y) return x;
    for (int i = lg[depth[x]] - 1; i >= 0; i--) {
        if (father[x][i] != father[y][i]) x = father[x][i], y = father[y][i];
    }
    return father[x][0];
}

int main()
{
    int n, m, s;
    cin >> n >> m >> s;
    for (int i = 1; i < n; i++) {
        int x, y;
        cin >> x >> y;
        ada(x, y);
        add(y, x);
    }
    for (int i = 1; i <= n; i++) {           ///预先算出log_2(i)+1的值
        lg[i] = lg[i - 1] + (1 << lg[i - 1] == i);///常数优化预处理,此操作为求出log2(i)的大小
    }
    BuildTree(s, 0);
    for (int i = 1; i <= m; i++) {
        int a, b;
        cin >> a >> b;
        cout << LCA(a, b) << endl;
    }
    return 0;
}

树上差分

#include<bits/stdc++.h>
using namespace std;
const int MAX = 6e5+10;
int head[100010], idx, fa[100010][34], dep[100010], p[100010], lg[100010];
struct Edge{int to, nxt;}e[MAX];
void add(int u, int v) {e[++idx].to = v, e[idx].nxt = head[u], head[u] = idx;}
int n, m;

void build(int u, int f)
{
    fa[u][0] = f;
    dep[u] = dep[f] + 1;
    for (int i = 1; i <= lg[dep[u]]; i++) {fa[u][i] = fa[fa[u][i - 1]][i - 1];}
    for (int i = head[u]; i; i = e[i].nxt) {
        int v = e[i].to;
        if (v != f) build(v, u);
    }
}

int LCA (int x, int y)
{
    if (dep[x] < dep[y]) swap(x, y);
    while (dep[x] > dep[y]) {x = fa[x][lg[dep[x] - dep[y]] - 1];}
    if (x == y) return x;
    for (int i = lg[dep[x]] - 1; i >= 0; i--) {
        if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
    }
    return fa[x][0];
}

void dfs(int u, int f)
{
    for (int i = head[u]; i; i = e[i].nxt) {
        int v = e[i].to;
        if (v == f) continue;
        dfs(v, u);
        p[u] += p[v];
    }
}

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++) lg[i] = lg[i - 1] + (1 << lg[i - 1] == i);
    for (int i = 1; i < n; i++) {
        int u, v;
        cin >> u >> v;
        add(u, v), add(v, u);
    }
    build(1, 0);
    for (int i = 1; i <= m; i++) {
        int a, b;
        cin >> a >> b;
        p[a]++, p[b]++;
        p[LCA(a, b)] -= 2;
    }
    dfs(1, 0);
    int ans = 0;
    for (int i = 2; i <= n; i++) {
        if (!p[i]) ans += m;
        if (p[i] == 1) ans++;
    }
    cout << ans;
    return 0;
}

LCA树剖

#include<bits/stdc++.h>
using namespace std;
const int MAX = 1e6+10;
int top[MAX], son[MAX], dfs_clock[MAX], fa[MAX], siz[MAX], dep[MAX];
int head[MAX], idx;
struct Edge{int to, nxt;}e[MAX << 1];
void add(int u, int v) {e[++idx].to = v, e[idx].nxt = head[u], head[u] = idx;}
int n, m, s;
int cnt;
void dfs1(int u, int f) 
{
    son[u] = -1;
    siz[u] = 1;
    dep[u] = dep[f] + 1;
    for (int i = head[u]; i; i = e[i].nxt) {
        int v = e[i].to;
        if (v == f) continue;
        fa[v] = u;
        dfs1(v, u);
        siz[u] += siz[v];
        if (son[u] == -1 || siz[v] > siz[son[u]]) son[u] = v;
    }
}
void dfs2(int u, int t)
{
    top[u] = t;
    cnt++;
    dfs_clock[u] = cnt;
    if (son[u] == -1) return;
    dfs2(son[u], t);
    for (int i = head[u]; i; i = e[i].nxt) {
        int v = e[i].to;
        if (v != son[u] && v != fa[u]) dfs2(v, v);
    }
}

int LCA(int x, int y) 
{
    while (top[x] != top[y]) {
        if (dep[top[x]] > dep[top[y]]) x = fa[top[x]];
        else y = fa[top[y]];
    }
    return dep[x] > dep[y] ? y : x;
}   

int main()
{
    cin >> n >> m >> s;
    for (int i = 1; i < n; i++) {int u, v; cin >> u >> v; add(u, v), add(v, u);}
    dfs1(s, 0);
    dfs2(s, s);
    while (m--) {
        int x, y;
        cin >> x >> y;
        cout << LCA(x, y) << endl;
    }
    return 0;
}

虚树

//https://www.luogu.com.cn/problem/P2495
//https://oi-wiki.org/graph/virtual-tree/
//虚树
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX = 1e6+10;
ll head[500050], idx, fa[500050][33], dep[500050], head1[500050], idx1, stac[500050], top;
ll query[500050], dfncnt, dfn[500050], lg[500050], minn[500050], h[500050];
struct Edge{ll to, nxt, d;}e[MAX], e1[MAX];
void add(ll u, ll v, ll d) {e[++idx].to = v, e[idx].nxt = head[u], e[idx].d = d, head[u] = idx;}
void vadd(ll u, ll v) {e1[++idx1].to = v, e1[idx1].nxt = head1[u], head1[u] = idx1;}
ll n, m;

void dfs(ll u, ll f)
{
    fa[u][0] = f;
    dep[u] = dep[f] + 1;
    dfn[u] = ++dfncnt;
    for (ll i = 1; i <= lg[dep[u]]; i++) {fa[u][i] = fa[fa[u][i - 1]][i - 1];}
    for (ll i = head[u]; i; i = e[i].nxt) {
        ll v = e[i].to;
        if (v == f) continue;
        minn[v] = min(minn[u], e[i].d);
        dfs(v, u);
    }
}

ll LCA (ll x, ll y)
{
    if (dep[x] < dep[y]) swap(x, y);
    while (dep[x] > dep[y]) x = fa[x][lg[dep[x] - dep[y]] - 1];
    if (x == y) return x;
    for (ll i = lg[dep[x] - 1]; i >= 0; i--) {
        if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
    }
    return fa[x][0];
}

ll dp(ll u)
{
    ll sum = 0, tmp;
    for (ll i = head1[u]; i; i = e1[i].nxt) {
        ll v = e1[i].to;
        sum += dp(v);
    }

    if (query[u]) tmp = minn[u];
    else tmp = min(minn[u], sum);
    query[u] = 0;head1[u] = 0;
    return tmp;
}

bool cmp(ll a, ll b){return dfn[a] < dfn[b];}
int main()
{
    cin >> n;
    minn[1] = 2147483647000000;
    for (ll i = 1; i <= n; i++) lg[i] = lg[i - 1] + (1 << lg[i - 1] == i);
    for (ll i = 1; i < n; i++) {
        ll u, v, d;
        cin >> u >> v >> d;
        add(u, v, d), add(v, u, d);
    }
    dfs(1, 0);
    cin >> m;
    while (m--) {
        idx1 = 0;
        ll k;
        cin >> k;
        for (ll i = 1; i <= k; i++) {cin >> h[i];query[h[i]] = 1;}
        sort(h + 1, h + 1 + k, cmp);
        stac[top = 1] = 1;
        for (ll i = 1; i <= k; i++) {
            if (h[i] == 1) continue;
            ll now = h[i];
            ll lca = LCA(now, stac[top]);
            if (lca != stac[top]) {
                while (dfn[lca] < dfn[stac[top - 1]]) {
                    vadd(stac[top - 1], stac[top]), top--;
                }
                if (lca != stac[top - 1]) {
                    head1[lca] = 0; vadd(lca, stac[top]); stac[top] = lca;
                }else vadd(lca, stac[top--]);
            }
            head1[now] = 0, stac[++top] = now;
        }
        for (ll i = 1; i < top; i++) {vadd(stac[i], stac[i + 1]);}
        cout << dp(1) << endl;
    }
    return 0;
}

Kruskal最小生成树

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX = 5e3+10;
int fa[MAX];
int n, m;
struct Edge {int from, to, w;}a[200005];
bool cmp(Edge A, Edge B){ return A.w < B.w;}
void ori(){ for (int i = 1; i <= n; i++) fa[i] = i;}
int find(int u){return u == fa[u] ? u : fa[u] = find(fa[u]);}

int ans = 0;
void Kruskal()
{
    sort(a + 1, a + 1 + m, cmp);
    for (int i = 1; i <= m; i++) {
        int x = a[i].from, y = a[i].to;
        x = find(x), y = find(y);
        if (x == y) continue;
        fa[x] = y;
        ans += a[i].w;
    }
}

int main()
{
    cin >> n >> m;
    ori();
    for (int i = 1; i <= m; i++) {
        cin >> a[i].from >> a[i].to >> a[i].w;
    }
    Kruskal();
    cout << ans << endl;
    return 0;
}

严格次小生成树

///https://www.luogu.com.cn/problem/P4180
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX = 6e5+10;
const ll INF = 2147483647000000;

ll ans = 0;
ll head[MAX], idx, dep[MAX], fa[MAX][35], num, lg[MAX], w1[MAX][35], w2[MAX][35], f[MAX];
struct Kruskal{ll u, v, d;}a[600010], mytry[600010];
struct Edge{ll to, nxt, d;}e[800010];
void add(ll u, ll v, ll d){e[++idx].to = v, e[idx].nxt = head[u], e[idx].d = d, head[u] = idx;}
bool cmp(Kruskal a, Kruskal b) {return a.d < b.d;}
ll find(ll n) {return n == f[n] ? n : f[n] = find(f[n]);}
void kruskal(ll m)
{
    sort(a + 1, a + m + 1, cmp);
    for (ll i = 1; i <= m; i++) {
        ll u = a[i].u, v = a[i].v;
        u = find(u), v = find(v);
        if (u == v) {mytry[++num] = a[i];continue;}
        f[u] = v;
        ans += a[i].d;
        add(a[i].u, a[i].v, a[i].d), add(a[i].v, a[i].u, a[i].d);
    }
}

void dfs(ll u, ll ff)
{
    dep[u] = dep[ff] + 1;
    fa[u][0] = ff;
    for (ll i = 1; i <= lg[dep[u]]; i++) {
        fa[u][i] = fa[fa[u][i - 1]][i - 1];
        w1[u][i] = max(w1[u][i - 1], w1[fa[u][i - 1]][i - 1]);
        w2[u][i] = max(w2[u][i - 1], w2[fa[u][i - 1]][i - 1]);
        if (w1[u][i - 1] < w1[fa[u][i - 1]][i - 1]) w2[u][i] = max(w2[u][i], w1[u][i - 1]);
        else if (w1[u][i - 1] > w1[fa[u][i - 1]][i - 1]) w2[u][i] = max(w2[u][i], w1[fa[u][i - 1]][i - 1]);
    }
    for (ll i = head[u]; i; i = e[i].nxt) {
        ll v = e[i].to;
        if (v == ff) continue;
        w1[v][0] = e[i].d;
        w2[v][0] = -INF;
        dfs(v, u);
    }
}

ll LCA(ll x, ll y)
{
    if (dep[x] < dep[y]) swap(x, y);
    while (dep[x] > dep[y]) { x = fa[x][lg[dep[x] - dep[y]] - 1];}
    if (x == y) return x;
    for (ll i = lg[dep[x]] - 1; i >= 0; i--) {if (fa[x][i] != fa[y][i]) {x = fa[x][i], y = fa[y][i];}}
    return fa[x][0];
}

ll cal(ll u, ll v, ll d)
{
    ll ans = -INF;
    while(dep[u] > dep[v]) {
        if (d == w1[u][lg[dep[u] - dep[v]] - 1]) ans = max(ans, w2[u][lg[dep[u] - dep[v]] - 1]);
            else ans = max(ans, w1[u][lg[dep[u] - dep[v]] - 1]);
            u = fa[u][lg[dep[u] - dep[v]] - 1];
    }
    return ans;
}

int main()
{
    ll n, m;
    cin >> n >> m;
    for (ll i = 1; i <= n; i++) lg[i] = lg[i - 1] + (1 << lg[i - 1] == i);
    for (ll i = 1; i <= n; i++) f[i] = i;
    for (ll i = 1; i <= m; i++) {
        ll u, v, d;
        cin >> u >> v >> d;
        a[i] = {u, v, d};
    }
    kruskal(m);
    w2[1][0] = -INF;
    dfs(1, 0);
    ll res = INF;
    for (ll i = 1; i <= num; i++) {
        ll u = mytry[i].u, v = mytry[i].v;
        ll l = LCA(u, v);
        ll max1 = cal(u, l, mytry[i].d);
        ll max2 = cal(v, l, mytry[i].d);
        res = min(res, ans - max(max1, max2) + mytry[i].d);
    }
    cout << res;
    return 0;
}

最大流 ISAP算法

详解

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF = 2147483647000000;
const int N = 20005, M = 100010;
struct Edge{ll to, nxt, d;}e[M];
ll head[N], idx = 1;
void add(ll u, ll v, ll d) {e[++idx].to = v, e[idx].nxt = head[u], e[idx].d = d, head[u] = idx;}
ll dep[N], gap[N], cur[N];
ll n, m, s, t;
ll maxflow;
void bfs()
{
    memset(dep, -1, sizeof(dep));
    memset(gap, 0, sizeof (gap));
    dep[t] = 0;
    gap[0] = 1;
    queue<ll> Q;
    Q.push(t);
    while (!Q.empty()) {
        ll u = Q.front(); Q.pop();
        for (ll i = head[u]; i; i = e[i].nxt) {
            ll v = e[i].to;
            if (dep[v] != -1) continue;
            dep[v] = dep[u] + 1;
            gap[dep[v]]++;
            Q.push(v);
        }
    }
}

ll dfs(ll u, ll flow)
{
    if (u == t) {
        maxflow += flow;
        return flow;
    }
    ll used = 0;
    for (ll& i = cur[u]; i; i = e[i].nxt) {
        ll v = e[i].to, d = e[i].d;
        if (d && dep[v] + 1 == dep[u]) {
            ll mi = dfs(v, min(flow - used, d));
            if(mi) {
                e[i].d -= mi;
                e[i ^ 1].d += mi;
                used += mi;
            }
            if (used == flow) return used;
        }
    }
    gap[dep[u]]--;
    if (gap[dep[u]] == 0) dep[s] = N + 1;
    dep[u]++;
    gap[dep[u]]++;
    return used;
}

ll ISAP()
{
    maxflow = 0;
    bfs();
    while (dep[s] < N) {memcpy(cur,head,sizeof(head));dfs(s, INF);}
    return maxflow;
}

int main()
{
    cin >> n >> m >> s >> t;
    for(ll i = 1; i <= m; i++) {
        ll u, v, w;
        cin >> u >> v >> w;
        add(u, v, w), add(v, u, 0);
    }
    cout << ISAP();
    return 0;
}

最小费用最大流 MCMF

#include<bits/stdc++.h>
using namespace std;
const int N = 5e3+10, M = 1e6+10;
int head[N], idx = 1;
struct Edge{int to, nxt, d, c;}e[M];
void add(int u, int v, int d, int c)
{
    e[++idx].to = v, e[idx].nxt = head[u], head[u] = idx, e[idx].d = d, e[idx].c = c;
    e[++idx].to = u, e[idx].nxt = head[v], head[v] = idx, e[idx].d = 0, e[idx].c = -c;
}
int dis[N], inq[N], incf[N], pre[N], n, m, s, t, maxflow, mincost;
///incf代表当前增广路上最小流量
bool spfa()
{
    queue<int> Q;
    memset(dis, 0x3f, sizeof (dis));
    memset(inq, 0, sizeof (inq));
    Q.push(s);
    dis[s] = 0;
    inq[s] = 1;
    incf[s] = 1 << 30;
    while (!Q.empty()) {
        int u = Q.front(); Q.pop();
        inq[u] = 0;
        for (int i = head[u]; i; i = e[i].nxt) {
            if (!e[i].d) continue;
            int v = e[i].to, d = e[i].c;
            if (dis[v] > dis[u] + d) {
                dis[v] = dis[u] + d;
                incf[v] = min(incf[u], e[i].d);
                pre[v] = i;
                if (!inq[v]) {inq[v] = 1; Q.push(v);}
            }
        }
    }
    return dis[t] != 0x3f3f3f3f;
}

void MCMF()
{
    while (spfa()) {
        int x = t;
        maxflow += incf[t];
        mincost += dis[t] * incf[t];
        int i;
        while (x != s) {
            i = pre[x];
            e[i].d -= incf[t];
            e[i ^ 1].d += incf[t];
            x = e[i ^ 1].to;
        }
    }
}

int main()
{
    cin >> n >> m >> s >> t;
    for (int i = 1; i <= m; i++) {
        int u, v, d, c;
        cin >> u >> v >> d >> c;
        add(u, v, d, c);
    }
    MCMF();
    cout << maxflow << ' ' << mincost;
    return 0;
}

树的重心

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAX = 2e5+10;
int n;

int head[MAX];
struct Edge{
    int to, nxt;
}edges[MAX];
int cnt;
void add(int u, int v)
{
    edges[++cnt].to = v;
    edges[cnt].nxt = head[u];
    head[u] = cnt;
    
    edges[++cnt].to = u;
    edges[cnt].nxt = head[v];
    head[v] = cnt;
}

int ans = INF;
int vis[MAX];
int dfs(int u)
{
    int res = 0;
    int sum = 1;
    for (int i = head[u]; i; i = edges[i].nxt) {
        int v =edges[i].to;
        if (!vis[v]) {
            vis[v] = 1;
            int s = dfs(v);
            res = max(res, s);
            sum += s;
        }
    }
    res = max(res, n - sum);
    ans = min(ans, res);
    return sum;
}

int main()
{
    cin >> n;
    for (int i = 0; i < n - 1; i++) {
        int u, v;
        cin >> u >> v;
        add(u, v);
    }
    vis[1] = 1;
    dfs(1);
    cout << ans;
    return 0;
}

图的中心

///https://www.luogu.com.cn/problem/CF266D
///https://oi-wiki.org/graph/mdst/
#include<bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAX = 205;
int f[MAX][MAX], rk[MAX][MAX], val[MAX];
int n, m;
struct Edge{int u, v, d;}a[100010];
void floyd()
{
    for(int k = 1; k <= n; k++) {
        for (int i = 1; i <= n; i++) {
            for(int j = 1; j <= n; j++) {
                if (f[i][k] < INF && f[k][j] < INF) {
                    f[i][j] = min(f[i][j], f[i][k] + f[k][j]);
                }
            }
        }
    }
}
bool cmp(int a, int b) {return val[a] < val[b];}
int main()
{
    cin >> n >> m;
    memset(f, 0x3f, sizeof(f));
    for (int i = 1; i <=n; i++) f[i][i] = 0;
    for (int i = 1; i <= m; i++) {
        cin >> a[i].u >> a[i].v >> a[i].d;
        f[a[i].u][a[i].v] = f[a[i].v][a[i].u] = min(f[a[i].u][a[i].v], a[i].d);
    }
    floyd();
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            rk[i][j] = j; val[j] = f[i][j];
        }
        sort(rk[i] + 1, rk[i] + 1 + n, cmp);
    }
    int ansp = INF, ansl = INF;
    ///绝对中心在点上
    for (int i = 1; i <= n; i++) ansp = min(ansp, f[i][rk[i][n]] * 2);
    绝对中心在边上
    for(int i = 1; i <= m; i++) {
        int u = a[i].u, v = a[i].v, d = a[i].d;
        for (int p = n, j = n - 1; j >= 1; j--) {
            if (f[v][rk[u][j]] > f[v][rk[u][p]]) {
                ansl = min(ansl, f[u][rk[u][j]] + f[v][rk[u][p]] + d);
                p = j;
            }
        }
    }
    printf("%.2lf", 1.0 * min(ansp, ansl) / 2);
    return 0;
}

树的直径

///https://www.luogu.com.cn/problem/P5536
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX = 3e5+10;
struct Edge{
    ll to, nxt;
}edges[501100];
ll cnt;
ll head[MAX];
void add(ll u, ll v)
{
    edges[++cnt].to = v;
    edges[cnt].nxt = head[u];
    head[u] = cnt;

    edges[++cnt].to = u;
    edges[cnt].nxt = head[v];
    head[v] = cnt;
}

struct Node {
    ll now, steps;
    Node(ll n, ll s):now(n), steps(s){}
    Node(){}
};
ll fa[MAX];
ll depth[MAX];
ll vis[MAX];
ll len = -1;
ll bfs(ll s)
{
    memset(fa, 0, sizeof (fa));
    fa[s] = s;
    memset(depth, 0, sizeof (depth)); memset(vis, 0, sizeof (vis));
    ll last;
    ll maxd = 0;
    queue<Node> Q;
    vis[s] = 1;
    depth[s] = 0;
    Q.push(Node(s, 0));
    while(!Q.empty()) {
        Node q = Q.front(); Q.pop();
        len = max(len, q.steps);
        for (int i = head[q.now]; i; i = edges[i].nxt) {
            int to = edges[i].to;
            if (vis[to]) continue;
            vis[to] = 1;
            depth[to] = depth[q.now] + 1;
            fa[to] = q.now;
            if(maxd < depth[to]) {
                last = to;
                maxd = depth[to];
            }
            Q.push(Node(to, q.steps + 1));
        }
    }
    return last;
}
ll maxdepth[MAX];
void dfs(ll now, ll fath)
{
    maxdepth[now] = depth[now];
    for (ll i = head[now]; i; i = edges[i].nxt) {
        ll to = edges[i].to;
        if (to == fath) continue;
        depth[to] = depth[now] + 1;
        dfs(to, now);
        maxdepth[now] = max(maxdepth[now], maxdepth[to]);
    }
}
ll ans[MAX];
ll Ans;

bool cmp(ll a, ll b)
{
    return a > b;
}

int main()
{
    ll n, k;
    cin >> n >> k;
    for(int i = 1; i < n; i++) {
        ll u, v;
        cin >> u >> v;
        add(u, v);
    }
    ll last = bfs(bfs(1));
    ll deep;
    ll node = last;
    for(ll i = 1; i <= (depth[last] + 1) / 2; i++) node = fa[node];
    memset(depth, 0, sizeof (depth));
    dfs(node, 0);
    for (ll i = 1; i <= n; i++) {
        ans[i] = maxdepth[i] - depth[i];
    }
    sort(ans + 1, ans + 1 + n, cmp);
    cout << ans[k + 1] + 1;
    return 0;
}

二分图最大匹配

#include<bits/stdc++.h>
using namespace std;
const int MAX = 1e5+10;
int head[505], idx;
struct Edge{int to, nxt;}e[MAX];
void add(int u, int v) {e[++idx].to = v, e[idx].nxt = head[u], head[u] = idx;}
int match[505], vis[505];
bool find(int u)
{
    for (int i = head[u]; i; i = e[i].nxt ) {
        int v = e[i].to;
        if (!vis[v]) {
            vis[v] = 1;
            if (!match[v] || find(match[v])) {
                match[v] = u;
                return true;
            }
        }
    }
    return false;
}
int n1, n2, m, ans;
int main()
{
    cin >> n1 >> n2 >> m;
    for (int i = 0; i < m; i++) {
        int u, v;
        cin >> u >> v;
        add(u, v);
    }
    for (int i = 1; i <= n1; i++) {
        memset(vis, 0, sizeof (vis));
        if (find(i)) ans++;
    }
    cout << ans;
    return 0;
}

DP

有依赖的背包

#include<bits/stdc++.h>
using namespace std;
const int N = 110, M = 1e5+10;
int head[N], idx;
struct Edge{int to, nxt;}e[M];
void add (int u, int v) {e[++idx].to = v, e[idx].nxt = head[u], head[u] = idx;}
int dp[110][10010], v[N], w[N];
int n, m;
void dfs(int u)
{
    for (int i = v[u]; i <= m; i++) dp[u][i] = w[u];
    for (int i = head[u]; i; i = e[i].nxt) {
        int x = e[i].to;
        dfs(x);
        for (int j = m; j >= v[u]; j--) {
            for (int k = 0; k <= j - v[u]; k++) {
                dp[u][j] = max(dp[u][j], dp[u][j - k] + dp[x][k]);
            }
        }
    }
}

int main()
{
    cin >> n >> m;
    int root;
    for (int i = 1; i <= n; i++) {
        int u;
        cin >> v[i] >> w[i] >> u;
        if (u != -1) add(u, i);
        if (u == -1) root = i;
    }
    dfs(root);
    cout << dp[root][m];
    return 0;
}

背包求方案数

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1000010;
const ll MOD = 1e9+7;
ll dp[N], v[N], w[N], cnt[N];
int main()
{
    ll n, m;
    cin >> n >> m;
    for (int i = 1; i <= m; i++) {
        cin >> v[i] >> w[i];
    }
    for (int i = 0; i <= 1050; i++) cnt[i] = 1;
    for (int i = 1; i <= n; i++) {
        for (int j = m; j >= v[i]; j--) {
            int tmp = dp[j - v[i]] + w[i];
            if (tmp > dp[j]) {
                dp[j] = tmp;
                cnt[j] = cnt[j - v[i]];
            }else if (tmp == dp[j]) {
                cnt[j] += cnt[j - v[i]];
                cnt[j] %= MOD;
            }
        }
    }
    cout << cnt[m];
    return 0;
}

背包求具体方案

#include<bits/stdc++.h>
using namespace std;
int dp[1010][1010], v[1010], w[1010], n, m;
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> v[i] >> w[i];
    for (int i = n; i >= 1; i--) {
        for (int j = 0; j <= m; j++) {
            dp[i][j] = dp[i + 1][j];
            if (j >= v[i]) dp[i][j] = max(dp[i][j], dp[i + 1][j - v[i]] + w[i]);
        }
    }
    int j = m;
    for (int i = 1; i <= n; i++) {
        if (j >= v[i] && dp[i][j] == dp[i + 1][j - v[i]] + w[i]) {
            cout << i << ' ';
            j -= v[i];
        }
    }
    return 0;
}

数据结构

KMP

#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
using namespace std;
char a[10005], b[10005];
int next[10005];

void GetNext(char *p, int next[])
{
	int pLen = strlen(p);
	next[0] = -1;
	int k = -1;
	int j = 0;
	while (j < pLen - 1) {
		if (k == -1 || p[j] == p[k]) {
			++j;
			++k;
			if (p[j] != p[k])
				next[j] = k; 
			else
				next[j] = next[k];
		}
		else {
			k = next[k];
		}
	}
}

int KmpSearch(char* s, char* p)
{
	int i = 0;
	int j = 0;
	int sLen = strlen(s);
	int pLen = strlen(p);
	while (i < sLen && j < pLen) {
		if (j == -1 || s[i] == p[j]) {
			i++;
			j++;
		}
		else {
			j = next[j];
		}
	}
	if (j == pLen)
		return i - j;
	else
		return -1;
}

int main()
{
    cin >> a >> b;
    GetNext(b, next);
    cout << KmpSearch(a, b);
    return 0;
}

ST表

它是解决RMQ问题(区间最值问题)的一种强有力的工具
它可以做到 O ( n l o g n ) O(nlogn) O(nlogn)预处理, O ( 1 ) O(1) O(1)查询最值

#include<bits/stdc++.h>
using namespace std;
#define sf(x) scanf("%d", &x)
#define pf(x) printf("%d\n", x)
const int MAX = 1e6+10;
int st[MAX][30];

int find(int l, int r)
{
	int k = log2(r - l + 1);
	return max(st[l][k], st[r - (1 << k) + 1][k]);
}

int main()
{
	int n, m;
	cin >> n >> m;
	for (int i = 1;  i<= n; i++) sf(st[i][0]);
	for (int j = 1;  j<= 26; j++) {
		for (int i = 1; i + (1 << j) - 1 <= n; i++) {
			st[i][j] = max(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
		}
	}
	while (m--) {
		int l, r;
		sf(l), sf(r);
		pf(find(l ,r));
	}
	return 0;
}

线段树

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX = 1e6+10;
ll a[MAX], tag[MAX << 1], ans[MAX << 1];

ll lt(ll x){return x << 1;}
ll rt(ll x){return x << 1 | 1;}

void push_up(ll x){ans[x] = ans[lt(x)] + ans[rt(x)];}
void build(ll x, ll l, ll r)
{
    tag[x] = 0;
    if (l == r) {ans[x] = a[l]; return;}
    ll mid = l + r >> 1;
    build(lt(x), l, mid);
    build(rt(x), mid + 1, r);
    push_up(x);
}

void f(ll x, ll l, ll r, ll k)
{
    tag[x] += k;
    ans[x] = ans[x] + k * (r - l + 1);
}
void push_down(ll x, ll l, ll r) 
{
    ll mid = l + r >> 1;
    f(lt(x), l, mid, tag[x]);
    f(rt(x), mid + 1, r, tag[x]);
    tag[x] = 0;
}
void update(ll nl, ll nr, ll l, ll r, ll x, ll k)
{
    if (nl <= l && nr >= r) {
        ans[x] += k * (r - l + 1);
        tag[x] += k;
        return;
    }
    push_down(x, l, r);
    ll mid = l + r >> 1;
    if (nl <= mid) {update(nl, nr, l, mid, lt(x), k);}
    if (nr > mid) {update(nl, nr, mid + 1, r, rt(x), k);}
    push_up(x);
}

ll query(ll nl, ll nr, ll l, ll r, ll x)
{
    ll res = 0;
    if (nl <= l && nr >= r) return ans[x];
    ll mid = l + r >> 1;
    push_down(x, l, r);
    if (nl <= mid) {res += query(nl, nr, l, mid, lt(x));}
    if (nr > mid) {res += query(nl, nr, mid + 1, r, rt(x));}
    return res;
}

int main()
{
    ll n, m;
    cin >> n >> m;
    for (ll i = 1; i <= n; i++) {
        scanf("%lld", &a[i]);
    }
    build(1, 1, n);
    while (m--) {
        ll op;
        scanf("%d", &op);
        if (op == 1) {
            ll x, y, k;
            scanf("%lld%lld%lld", &x, &y, &k);
            update(x, y, 1, n, 1, k);
        }
        else {
            ll x, y;
            scanf("%lld%lld", &x, &y);
            printf("%lld\n", query(x, y, 1, n, 1));
        }
    } 
    return 0;
}

字典树

#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long ll;
const int MAX = 1e5+10;
ll son[MAX * 33][2], idx;

void insert(ll x)
{
    ll p = 0;
    for (ll len = 31; len >= 0; len--) {
        ll y = x >> len & 1;
        if (!son[p][y]) son[p][y] = ++idx;
        p = son[p][y];
    }
}

ll query(ll q)
{
    ll p = 0;
    ll ans = 0;
    for(int len = 31; len >= 0; len--) {
        ll y = q >> len & 1;
        if (son[p][!y]) {
            ans = (ans << 1) + !y;
            p = son[p][!y];
        }else {
            ans = (ans << 1) + y;
            p = son[p][y];
        }
    }
    return ans;
}

int main()
{
    ll t;
    scanf("%lld", &t);
    for (ll tt = 1; tt <= t; tt++) {
        memset(son, 0, sizeof(son));idx = 0;
        ll n, m;
        scanf("%lld%lld", &n, &m);
        printf("Case #%lld:\n", tt);
        for(int i = 1; i <= n; i++) {
            ll x;
            scanf("%lld", &x);
            insert(x);
        }
        while (m--) {
            ll q;
            scanf("%lld", &q);
            printf("%lld\n", query(q));
        }

    }
    return 0;
}

数论

欧拉筛

void init()
{
    for (int i = 1; i < N; i++) phi[i] = i;
    for (int i = 2; i < N; i++) {
        if (phi[i] == i) {
            for (int j = i; j < N; j += i) {
                phi[j] = phi[j] / i *(i - 1);
            }
        }
    }
}

容斥原理

复杂度: O ( n 2 n ) O(n2^n) O(n2n)

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int MOD = 1e9+7;
int a[25], down = 1, n, m;

int ksm(int aa, int b)
{
    int ans = 1;
    while (b) {
        if (b & 1) ans = (ans * aa) % MOD;
        aa = (aa * aa) % MOD;
        b >>= 1;
    }return ans;
}

int C(int aa, int b)
{
    if (b > aa)return 0;
    int up = 1;
    for (int i = aa; i > aa - b; i--) {up = (i % MOD * up % MOD);}
    return up * down % MOD;
}

signed main()
{
    cin >> n >> m;
    for (int i = 0; i < n; i++) {cin >> a[i];}
    for (int i = 1; i < n; i++) {
        down = (down * i) % MOD;
    }
    down = ksm(down, MOD - 2);
    int ans = 0;
    for (int i = 0; i < (1 << n); i++) {
        int sign = 1;
        int d = m + n - 1, up = n - 1;
        for (int j = 0; j < n; j++) {
            if (i & (1 << j)) {
                sign = -sign;
                d -= a[j] + 1;
            }
        }
        ans = (ans + C(d, up) * sign) % MOD;
    }cout << (ans + MOD) % MOD;
    return 0;
}

线性筛筛莫比乌斯函数

题意:给出abd,求有多少对xy满足, x ≤ a x\le a xa​, y ≤ b y\le b yb​,并且 g c d ( x , y ) = d gcd(x,y)=d gcd(x,y)=d

思路:容斥配莫比乌斯函数优化一下

#include<bits/stdc++.h>
using namespace std;
#define sf(x) scanf("%lld", &x)
#define pf(x) printf("%lld\n", x)
#define int long long
const int N = 5e4+10;
int primes[N], cnt;
int st[N];
int mobius[N], sum[N];


/// 线性筛筛莫比乌斯函数
void init(int n)
{
    mobius[1] = 1;
    for (int i = 2; i <= n; i++) {
        if (!st[i]) {
            primes[cnt++] = i;
            mobius[i] = -1;
        }
        for (int j = 0; primes[j] * i <= n; j++) {
            int t = primes[j] * i;
            st[t] = 1;
            if (i % primes[j] == 0) {
                mobius[t] = 0;
                break;
            }
            mobius[t] = mobius[i] * -1;
        }
    }
    for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + mobius[i];
}
///有个东西,就是[a/x]=[a/g(x)],其中g(x)是满足这个式子的最大的x值,而g(x)=[a/x],这个不会超过根号n,直接枚举每个区间的r
signed main()
{
    init(N - 1);
    int T;
    cin >> T;
    while (T--) {
        int a, b, d;
        sf(a), sf(b), sf(d);
        a /= d, b /= d;
        int n = min(a, b);
        int res = 0;
        for (int l = 1, r; l <= n; l = r + 1) {
            r = min(n, min(a / (a / l), b / (b / l)));
            res += (sum[r] - sum[l - 1]) * (a / l) * (b / l);
        }pf(res);
    }
    return 0;
}

莫比乌斯反演

一:

F ( n ) = ∑ d ∣ n f ( d ) F(n) = \sum_{d|n}f(d) F(n)=dnf(d)

$\Rightarrow $

f ( n ) = ∑ d ∣ n F ( d ) ∗ μ ( n d ) f(n)=\sum_{d|n}F(d)*μ(\frac{n}{d}) f(n)=dnF(d)μ(dn)

二:

F ( n ) = ∑ n ∣ d f ( d ) F(n)=\sum_{n|d}f(d) F(n)=ndf(d)

$\Rightarrow $

f ( n ) = ∑ n ∣ d μ ( d n ) ∗ F ( d ) f(n)=\sum_{n|d}μ(\frac{d}{n})*F(d) f(n)=ndμ(nd)F(d)

三:
[ g c d ( i , j ) = 1 ] = ∑ d ∣ g c d ( i , j ) μ ( d ) [gcd(i,j)=1]=\sum_{d|gcd(i,j)}μ(d) [gcd(i,j)=1]=dgcd(i,j)μ(d)

∑ d ∣ n μ ( d ) = [ n = 1 ] \sum_{d|n}μ(d)=[n=1] dnμ(d)=[n=1]

Lucas定理

#include<bits/stdc++.h>
#define int long long
int f[200010], Inv[200010];
void solve()
{
    int n, m, p;
    std::cin >> n >> m >> p;
    f[0] = f[1] = 1, Inv[0] = Inv[1] = 1;
    for (int i = 2; i <= p; i++) {
        f[i] = f[i - 1] * i % p;
        Inv[i] = (p - (p / i)) * Inv[p % i] % p;
    }

    std::function<int(int, int)>C = [&](int m, int n) {
        return m < n ? 0ll : f[m] * Inv[f[n]] % p * Inv[f[m - n]] % p;
    };
    std::function<int(int ,int)>lucas = [&](int m, int n) {
        return n == 0 ? 1ll : lucas(m / p, n / p) * C(m % p, n % p) % p;
    };
    std::cout << lucas(m + n, n) << "\n";
}

signed main()
{
    int T;
    std::cin >> T;
    while (T--) solve();
    return 0;
}

Lucas 二

#include<bits/stdc++.h>
#define int long long
using namespace std;
int f[1000010], Inv[1000010], p;

int ksm(int a, int b)
{
    int res = 1;
    while (b) {
        if (b & 1) {res = res * a % p;}
        a = a * a % p;
        b >>= 1;
    }return res;
}

int binom(int a, int b)
{
    if (b < 0 || b > a) return 0;
    return f[a] * Inv[b] % p * Inv[a - b] % p;
}

void solve()
{
    int n, m;
    cin >> n >> m;
    int ans = 1;
    while (n > 0 || m > 0) {
        ans = ans * binom(n % p, m % p) % p;
        n /= p, m /= p;
    }
    cout << ans << "\n";
}

signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    int T;
    cin >> p >> T;
    f[0] = f[1] = 1;
    for (int i = 2; i <= p; i++) {
        f[i] = f[i - 1] * i % p;
    }
    Inv[p - 1] = ksm(f[p - 1], p - 2);
    for (int i = p - 1; i >= 1; i--) Inv[i - 1] = Inv[i] * i % p;
    while (T--) solve();
    return 0;
}

中国剩余定理

#include<bits/stdc++.h>
#define i64 long long

int main()
{
    int n;
    std::cin >> n ;
    std::vector<i64>a(n + 2), b(n + 2);
    for (int i = 1; i <= n; i++) {
        std::cin >> a[i] >> b[i];
    }
    std::function<i64(i64, i64, i64 &, i64 &)>exgcd = [&](i64 a, i64 b, i64 &x, i64 &y) {
        if (b == 0) {
            x = 1, y = 0; return a;
        }
        i64 pa = exgcd(b, a % b, y, x);
        y -= (a / b) * x;
        return pa;
    };
    std::function<i64(i64, i64)> inv = [&](i64 a, i64 p) {
        i64 x, y;
        exgcd(a, p, x, y);
        return (x + p) % p;
    };
    std::function<i64()>CRT = [&]{
        i64 p = 1, res = 0;
        for (int i = 1; i <= n; i++) p *= a[i];
        for (int i = 1; i <= n; i++) {
            i64 pa = p / a[i];
            res += (b[i] * pa * inv(pa, a[i])) % p;
        }return res % p;
    };

    std::cout << CRT();
    return 0;
}

FFT 递归

#include<bits/stdc++.h>
typedef std::complex<double> comp;
const int M = 1e6+10;
const comp I(0, 1);
const double PI = acos(-1);
comp A[M * 3], B[M * 3], tmp[M * 3], ans[M * 3];

void fft(comp F[], int N, int sgn = 1)
{
    if (N == 1) return;
    memcpy(tmp, F, sizeof (comp) * N);
    for (int i = 0; i < N; i++) {
        if (i & 1) F[i / 2 + N / 2] = tmp[i];
        else F[i / 2] = tmp[i];
    }
    fft(F, N / 2, sgn), fft(F + N / 2, N / 2, sgn);
    comp *G = F, *H = F + N / 2;
    comp cur = 1, step = exp(2 * PI / N * sgn * I);
    for (int k = 0; k < N / 2; k++) {
        tmp[k] = G[k] + cur * H[k];
        tmp[k + N / 2] = G[k] - cur * H[k];
        cur *= step;
    }
    memcpy(F, tmp, sizeof (comp) * N);
}

int main()
{
    int n, m, N;
    std::cin >> n >> m;
    N = 1 << std::__lg(n + m + 1) + 1;
    for (int i = 0; i <= n; i++) std::cin >> A[i];
    for (int i = 0; i <= m; i++) std::cin >> B[i];
    fft(A, N), fft(B, N);
    for (int i = 0; i < N; i++) {ans[i] = A[i] * B[i];}
    fft(ans, N, -1);
    for (int i = 0; i <= n + m; i++) {
        std::cout << int(ans[i].real() / N + 0.1) << ' ' ;
    }
    return 0;
}

FFT 蝴蝶变化优化

#include<bits/stdc++.h>
typedef std::complex<double> comp;
const int M = 1e6+10;
const comp I(0, 1);
const double PI = acos(-1);
double A[M * 3], B[M * 3];
int rev[M * 3];
comp F[M * 3];
void fft(comp F[], int N, int sgn = 1)
{
    for (int i = 0; i < N; i++) {
        if (i < rev[i]) swap(F[i], F[rev[i]]);
    }
    for (int l = 1; l < N; l <<= 1) {
        comp step = comp(cos(PI / l), sgn * sin(PI / l));
        for (int i = 0; i < N; i += l * 2) {
            comp cur(1, 0);
            for (int k = i; k < i + l; k++) {
                comp g = F[k], h = F[k + l] * cur;
                F[k] = g + h, F[k + l] = g - h;
                cur *= step;
            }
        }
    }
}
int main()
{
    int n, m, N;
    std::cin >> n >> m;
    N = 1 << std::__lg(n + m + 1) + 1;
    int bit = std::__lg(N);
    for (int i = 0; i < N; i++) {
        rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1));
    }
    for (int i = 0; i <= n; i++) std::cin >> A[i];
    for (int i = 0; i <= m; i++) std::cin >> B[i];
    for (int i = 0; i <= std::max(n, m); i++) {
        F[i] = comp(A[i], B[i]);
    }
    fft(F, N);
    for (int i = 0; i < N; i++) {
        F[i] = F[i] * F[i];
    }
    fft(F, N, -1);
    for (int i = 0; i <= n + m; i++) {
        std::cout << int(F[i].imag() / (N * 2) + 0.1) << ' ' ;
    }
    return 0;
}

NTT 优化的FFT

#include<bits/stdc++.h>
#define int long long
const int M = 1e6+10, P = 998244353, G = 3, Gi = 332748118;
int A[M * 3], B[M * 3], F[M * 3];
int rev[M * 3];

int ksm(int a, int b)
{
    int res = 1;
    while (b) {
        if (b & 1) res = res * a % P;
        a = a * a % P;
        b >>= 1;
    }return res;
}

void ntt(int F[], int N, int sgn = 1)
{
    for (int i = 0; i < N; i++) {
        if (i < rev[i]) std::swap(F[i], F[rev[i]]);
    }
    for (int l = 1; l < N; l <<= 1) {
        int step = ksm(sgn == 1 ? G : Gi, (P - 1) / (l << 1));
        for (int i = 0; i < N; i += l * 2) {
            int cur = 1;
            for (int k = i; k < i + l; k++) {
                int g = F[k], h = F[k + l] * cur % P;
                F[k] = (g + h) % P, F[k + l] = (g - h + P) % P;
                cur = cur * step % P;
            }
        }
    }
}

signed main()
{
    int n, m, N;
    std::cin >> n >> m;
    N = 1 << std::__lg(n + m + 1) + 1;
    int bit = std::__lg(N);

    for (int i = 0; i < N; i++) {
        rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1));
    }
    for (int i = 0; i <= n; i++) std::cin >> A[i];
    for (int i = 0; i <= m; i++) std::cin >> B[i];
    ntt(A, N), ntt(B, N);
    for (int i = 0; i < N; i++) {
        F[i] = A[i] * B[i] % P;
    }
    ntt(F, N, -1);
    int inv = ksm(N, P - 2);
    for (int i = 0; i <= n + m; i++) {
        std::cout << inv * F[i] % P << ' ' ;
    }
    return 0;
}

高精度

#include<bits/stdc++.h>
#define int long long
const int M = 1e6+10, P = 998244353, G = 3, Gi = 332748118;
int A[M * 3], B[M * 3], F[M * 3];
int rev[M * 3];

int ksm(int a, int b)
{
    int res = 1;
    while (b) {
        if (b & 1) res = res * a % P;
        a = a * a % P;
        b >>= 1;
    }return res;
}

void ntt(int F[], int N, int sgn = 1)
{
    for (int i = 0; i < N; i++) {
        if (i < rev[i]) std::swap(F[i], F[rev[i]]);
    }
    for (int l = 1; l < N; l <<= 1) {
        int step = ksm(sgn == 1 ? G : Gi, (P - 1) / (l << 1));
        for (int i = 0; i < N; i += l * 2) {
            int cur = 1;
            for (int k = i; k < i + l; k++) {
                int g = F[k], h = F[k + l] * cur % P;
                F[k] = (g + h) % P, F[k + l] = (g - h + P) % P;
                cur = cur * step % P;
            }
        }
    }
}

signed main()
{
    int n, m, N;
    std::string a, b;
    std::cin >> a >> b;
    n = a.length(), m = b.length();
    for (int i = 0; i < n; i++) A[i] = a[n - i - 1] - '0';
    for (int i = 0; i < m; i++) B[i] = b[m - 1 - i] - '0';
    N = 1 << std::__lg(n + m + 1) + 1;
    int bit = std::__lg(N);
    for (int i = 0; i < N; i++) {
        rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1));
    }
    ntt(A, N), ntt(B, N);
    for (int i = 0; i < N; i++) {
        F[i] = A[i] * B[i] % P;
    }
    ntt(F, N, -1);
    int inv = ksm(N, P - 2);
    std::vector<int> ans(N + 1);
    for (int i = 0; i <= N; i++) {
        ans[i] += (inv * F[i] % P);
        ans[i + 1] += ans[i] / 10;
        ans[i] %= 10;
    }
    int lim = N;
    while (!ans[lim] && lim >= 1) lim--;
    lim++;
    while (--lim >= 0) std::cout << ans[lim];
    return 0;
}

分治FFT

类比cdq分治

#include<bits/stdc++.h>
#define int long long
const int M = 1e5+10;
const int P = 998244353, G = 3;
int rev[M << 2], f[M << 2], g[M << 2], n, a[M << 2], b[M << 2];
void init(int bit)
{
    int N = 1 << bit;
    for (int i = 0; i < N; i++) {
        rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1));
    }
}
int ksm(int x, int y)
{
    int res = 1;
    while (y) {
        if (y & 1) res = res * x % P;
        x = x * x % P;
        y >>= 1;
    }return res;
}
const int Gi = 332748118;
void ntt(int *F, int N, int sgn = 1)
{
    for (int i = 0; i < N; i++) {
        if (i < rev[i]) std::swap(F[i], F[rev[i]]);
    }
    for (int l = 1; l < N; l <<= 1) {
        int step = ksm(sgn == 1 ? G : Gi, (P - 1) / (l << 1));
        for (int i = 0; i < N; i += (l << 1)) {
            int cur = 1;
            for (int k = i; k < l + i; k++, cur = cur * step % P) {
                int g = F[k], h = F[k + l] * cur % P;
                F[k] = (g + h) % P, F[k + l] = (g - h + P) % P;
            }
        }
    }
}

void solve(int l, int r, int bit)
{
    if (bit <= 0) return;
    if (l >= n) return ;
    int mid = (l + r) >> 1;
    solve(l, mid, bit - 1);
    init(bit);
    memset(a + (r - l) / 2, 0, sizeof (int) * (r - l) / 2);
    memcpy(a, f + l, (r - l) / 2 * sizeof (int));
    memcpy(b, g, sizeof(int) * (r - l));
    ntt(a, 1 << bit), ntt(b, 1 << bit);
    for (int i = 0; i < r - l; i++) a[i] = a[i] * b[i] % P;
    ntt(a, 1 << bit, -1);
    int N = 1 << bit;
    int inv = ksm(r - l, P - 2);
    for (int i = 0; i < r - l; i++) a[i] = a[i] * inv % P;
    for (int i = (r - l) / 2; i < (r - l); i++) {
        f[i + l] = (f[i + l] + a[i]) % P;
    }
    solve(mid, r, bit - 1);
}


signed main()
{
    std::cin >> n;
    int N = 1, bit = 0;
    while (N < n) N <<= 1, bit++;
    for (int i = 1; i < n; i++) std::cin >> g[i];
    f[0] = 1;
    solve(0, N, bit);
    for (int i = 0; i < n; i++) {
        std::cout << (f[i] + P) % P << ' ' ;
    }
    return 0;
}

大数质因子分解

const int S = 8; // 随机算法判定次数,8~10 就够了

// 龟速乘
    long long mult_mod(long long a, long long b, long long c) {
        a %= c, b %= c;
        long long ret = 0;
        long long tmp = a;
        while (b) {
            if (b & 1) {
                ret += tmp;
                if (ret > c) ret -= c;
            }
            tmp <<= 1;
            if (tmp > c) tmp -= c;
            b >>= 1;
        }
        return ret;
    }

    // 快速幂
    long long qow_mod(long long a, long long n, long long _mod) {
        long long ret = 1;
        long long temp = a % _mod;
        while (n) {
            if (n & 1) ret = mult_mod(ret, temp, _mod);
            temp = mult_mod(temp, temp, _mod);
            n >>= 1;
        }
        return ret;
    }

    // 是合数返回true,不一定是合数返回false
    bool check(long long a, long long n, long long x, long long t) {
        long long ret = qow_mod(a, x, n);
        long long last = ret;
        for (int i = 1; i <= t; i++) {
            ret = mult_mod(ret, ret, n);
            if (ret == 1 && last != 1 && last != n - 1) return true;
            last = ret;
        }
        if (ret != 1) return true;
        return false;
    }

    // 是素数返回true,不是返回false
    mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
    bool Miller_Rabin(long long n) {
        if (n < 2) return false;
        if (n == 2) return true;
        if ((n & 1) == 0) return false;
        long long x = n - 1;
        long long t = 0;
        while ((x & 1) == 0) { x >>= 1; t++; }

        for (int i = 0; i < S; i++) {
            long long a = rng() % (n - 1) + 1;
            if (check(a, n, x, t))
                return false;
        }

        return true;
    }

    long long factor[100];// 存质因数
    int tol; // 质因数的个数,0~tol-1

    long long gcd(long long a, long long b) {
        long long t;
        while (b) {
            t = a;
            a = b;
            b = t % b;
        }
        if (a >= 0) return a;
        return -a;
    }

    long long pollard_rho(long long x, long long c) {
        long long i = 1, k = 2;
        long long x0 = rng() % (x - 1) + 1;
        long long y = x0;
        while (1) {
            i++;
            x0 = (mult_mod(x0, x0, x) + c) % x;
            long long d = gcd(y - x0, x);
            if (d != 1 && d != x) return d;
            if (y == x0) return x;
            if (i == k) { y = x0; k += k; }
        }
    }
    // 对n质因数分解,存入factor,k一般设置为107左右
    void findfac(long long n, int k) {
        if (n == 1) return;
        if (Miller_Rabin(n)) {
            factor[tol++] = n;
            return;
        }
        long long p = n;
        int c = k;
        while (p >= n) p = pollard_rho(p, c--);
        findfac(p, k);
        findfac(n / p, k);
    }
    vector<int> fac(long long n) {
        tol = 0;
        vector<int>ret;
        findfac(n, 107);
        for (int i = 0; i < tol; i++)ret.push_back(factor[i]);
        return ret;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值