# 板子

### 最大公约数

ll Gcd(ll a, ll b) { return a % b == 0 ? b : Gcd(b, a % b); }


### 欧拉函数

//欧拉函数：求出小于等于n的  与n互质的个数，如果求多个数的欧拉值则要 筛法欧拉函数
using ll = long long;
ll Euler(ll n) {
ll ans = n;
for (int i = 2; i * i <= n; ++i) {
if (n % i == 0) {
ans = ans / i * (i - 1);
while (n % i == 0) n /= i;
}
}
if (n > 1) ans = ans / n * (n - 1);
return ans;
}


### 日期计算-基姆拉尔森

int Day(int y, int m, int d) {//由日期计算周几
if (m == 1 || m == 2)  m += 12, y -= 1;
return (d + 2 * m + 3 * (m + 1) / 5 + y + y / 4 - y / 100 + y / 400 + 1) % 7;
}


### 快速幂

typedef long long ll;
const ll mod = 1e9 + 7;
ll qpow(ll a,ll b) {
ll ans = 1; a %= mod;
for (; b; a = a * a % mod,b >>= 1)
if (b & 1)ans = ans * a % mod;
return ans;
}


### 快速乘

inline ll ksc(ll x, ll y, ll mod) {
ll res = 0;
for (; y; y >>= 1, x = (x << 1) % mod)
if (y & 1) res = (res + x) % mod;
return res;
}
//O(log n)

// O(1)快速乘（防爆long long）
typedef long long ll;
typedef unsigned long long ull;
typedef long double lb;
//代码压缩
inline ll ksc(ull x, ull y, ll p) {
return (x * y - (ull)((lb)x / p * y) * p + p) % p;
}


### 二分（返回第一个等于x的元素的下标）

int found(int a[],int left,int right,int x) {
while (left < right) {
int mid = (right + left) >> 1;
if (a[mid] < x) left = mid + 1;
else right = mid;
}
return left;
}


### 并查集

int f[maxn];
void init() { for (int i = 1; i <= n; ++i)f[i] = i; }
int find(int x) { return f[x] == x ? x : f[x] = find(f[x]); }
void merge(int x, int y) {
x = find(x), y = find(y);
if (x != y)  f[y] = x;
}
...


### 带权并查集

#include<bits/stdc++.h>
using namespace std;

const int maxn = 1e6 + 10;

int n, m;//n个顶点 m条边
int father[maxn]; //元素父亲节点
int Size[maxn];//集合大小
int maxs[maxn];//集合最大值
int dist[maxn];//元素x到它所在集合根节点的距离
int setNums = 0;//集合总数

//初始化
void init() {
setNums = n;
for (int i = 1; i <= n; i++)
father[i] = maxs[i] = i, Size[i] = 1, dist[i] = 0;
}

//查找
int find(int x) {
if (father[x] == x) return x;
int y = father[x];
father[x] = find(y);
dist[x] += dist[y]; //x到根的距离 需要加上y到根的距离
return father[x];
}

//合并
void join(int x, int y) {
int a = find(x), b = find(y);
if (a != b) {
setNums--;
father[a] = b;
dist[a] = Size[b];
Size[b] += Size[a];
maxs[b] = max(maxs[a], maxs[b]);
}
}

//查询集合总数量
int findSetnum() {
return setNums;
}

//查询x所在集合的大小(元素数量)
int findSize(int x) {
return Size[find(x)];
}

//查询x所在集合中的最大值
int findSetMax(int x) {
return maxs[find(x)];
}

//查询x到它的集合的根 的距离
int findDist(int x) {
return dist[x];
}

int main() {

return 0;
}


### 链式前向星

struct Edge {
int next, to;
LL dis;
}edges[maxn];

void add_edge(int from, int to, LL dis) {
num++;
edges[num].to = to;
edges[num].dis = dis;
}

for (int i = head[u]; i != 0; i = edges[i].next) {
...
}


### 邻接矩阵

struct Edge {
int from, to, dist;
//边的起点，终点，长度
Edge(int u,int v,int d):from(u),to(v),dist(d){}
//构造函数，用于边的初始化
};

struct HeapNode {
int d, u;//将结点的d值与结点捆绑在一起形成结构体，当然也可以用pair<int,int>代替
bool operator < (const HeapNode& rhs) const {
return d > rhs.d;
//当d>rhs.d为真时，优先级this<rhs.d成立，即d值小的优先级更大
}
};

void init(int n) {//初始化
num=0;//边在vector中的编号
for (int i = 0; i < n; i++) G[i].clear();
edges.clear();
//个人习惯，为了计数都能从1开始，先塞一个无关紧要的元素
edges.push_back(Edge(0, 0, 0));
//但G[i]计数还是从0开始
}

void AddEdge(int from, int to, int dist) {
edges.push_back(Edge(from, to, dist));
//调用Edge结构体中的构造函数，生成一条边并加入到Edge中
G[from].push_back(++num);
}


### 拓扑排序

int n, m, head[N], tot, deg[N], cnt;
vector<int>a;
struct node {
int v, next;
}e[N << 1];

void add(int u, int v) {
e[++tot].v = v;
deg[v]++;//入度
}

void topsort() {
queue<int>q;
for (int i = 1; i <= n; ++i)
if (deg[i] == 0)q.push(i);
while (q.size()) {
int x = q.front(); q.pop();
a[++cnt] = x;
for (int i = head[x]; i; i = e[i].next) {
int y = e[i].v;
if (--deg[y] == 0)q.push(y);
}
}
}

int main() {
cin >> n >> m;//点数、边数
for (int i = 1; i <= m; ++i) {
int x, y;//有向边
cin >> x >> y;
}
topsort();
for (int i = 1; i <= a.size(); ++i)
cout << a[i] << " ";
cout << endl;
}


### 最短路（Floyd）

//初始化
for (int i = 0;i < maxn ; i++) for (int j = 0;j< maxn ; j++)
d[i][j] = (i == j) ? 0 : INF;

for (int k = 0; k < n; k++)
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);

//传递闭包
for (int k = 0; k < n; k++)
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
d[i][j] |= d[i][k] & d[k][j];


### 最短路（SPFA）

int cnt[maxn];
bool inq[maxn];
int d[maxn];

bool spfa(int s) {
queue<int>Q;
memset(inq, 0, sizeof(inq));//标记结点是否在队列中
memset(cnt, 0, sizeof(cnt));
for (int i = 0; i < n; i++) d[i] = INF;
d[s] = 0; inq[s] = true;//标记结点s已在队列中
Q.push(s);//入队
while (!Q.empty()) {
int u = Q.front(); Q.pop();
inq[u] = false;//标记已不在队列
for (int i = 0; i < G[u].size(); i++) {
Edge& e = Edges[G[u][i]];//遍历以结点u为起点的有向边
if (d[u]<INF && d[u] + e.dist<d[e.to]) {
d[e.to] = d[u] + e.dist;//松弛
p[e.to] = G[u][i];//记录父节点
if (!inq[e.to]) {//只要不在队列中就可以入队
Q.push(e.to);
inq[e.to] = true;
if (++cnt[e.to] > n) return false;
//如果某个点迭代了超过n次，说明存在可以无限缩短的最短路，即负环
}
}
}
}
return true;
}


### 最短路（朴素Dijkstra）

int dis[maxn];
bool vis[maxn];

void dijkstra() {
// memset(vis,false);
// memset(dis,inf);

for (int i = 1; i <= n; i++)
dis[i] = inf, vis[i] = 0, a[i][i] = 0;  //初始化各数组
dis[s] = 0;
vis[s] = true;               // s作为起点
for (int i = 1; i < n; i++)  //重复n-1次
{
int x = -1;  //寻找的最小边点
for (int j = 1; j <= n; j++)
if (!vis[j] &&
(x == 0 || dis[j] < dis[x]))  //寻找未访问过且离起点最近的点
x = j;
if (x == -1)
break;
vis[x] = 1;
for (int y = 1; y <= n; y++)  //第一轮dis[y]中由起点可到达的点得到更新
dis[y] = min(dis[y], dis[x] + a[x][y])
}
}


### 最短路（堆优化Dijkstra）

#include <bits/stdc++.h>
using namespace std;
const int N   = 1e4 + 10;
const int inf = 0x3f3f3f3f;
vector<pair<int, int>> e[N];
priority_queue<pair<int, int>> q;
int n, m, s;
int dist[N];
bool vis[N];
void dijkstra() {
for (int i = 1; i <= n; ++i) dist[i] = inf, vis[0];
dist[s] = 0;
q.push(make_pair(0, s));
while (q.size()) {
int x = q.top().second; // 输出队头
q.pop();
if (vis[x]) continue; // 如果点x访问过，跳过，访问下一个队头
vis[x] = true;
for (int i = 0; i < e[x].size(); ++i) {
int v = e[x][i].first, w = e[x][i].second;
if (dist[v] > dist[x] + w) {
dist[v] = dist[x] + w;
q.push(make_pair(-dist[v], v));
}
}
}
}
int main() {
cin >> n >> m >> s;
for (int i = 1, u, v, w; i <= m; ++i) {
cin >> u >> v >> w;
e[u].push_back({v, w});
// e[v].push_back({u, w});
}
dijkstra();
for (int i = 1; i <= n; ++i) cout << dist[i] << " ";
cout << "\n";
return 0;
}


#include <cstdio>
#include <iostream>
#include <queue>
using namespace std;
typedef long long ll;
const int N = 1e4 + 10;  // 对应顶点的个数
const int M = 5e5 + 5;   //对应边的个数
const int inf = 2147483647;
priority_queue<pair<int, int>> q;
struct node {
int nxt, v, w;
} edge[N];    //边取M，其余取N
bool vis[N];  //这里的标记数组与spfa的vis数组含义不同，这里标记是否入过队列
int k = 0;                       // tot 总边数
int n, m, s, e;                  // s作为起点，e作为终点
void add(int u, int v, int w) {  //邻接表存储图
edge[k].v = v;
edge[k].w = w;
}

void dijkstra() {
for (int i = 1; i <= n; ++i)  // 初始化 vis d数组
d[i] = inf, vis[i] = 0;
d[s] = 0;  // s作为起点
q.push(make_pair(0, s));
while (q.size()) {
int x = q.top().second;  //输出队头
q.pop();
if (vis[x])
continue;  //如果点x访问过，跳过，访问下一个队头
vis[x] = true;
for (int i = head[x]; i; i = edge[i].nxt) {
int v = edge[i].v, w = edge[i].w;
if (d[v] > d[x] + w) {  //松弛操作，更新距离
d[v] = d[x] + w;
//把更新的距离和点入队，这里距离取负变成小根堆
q.push(make_pair(-d[v], v));
}
}
}
}

int main() {
// freopen("in.txt","r",stdin);
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
cin >> n >> m >> s;
for (int i = 1; i <= m; ++i) {
int u, v, w;
cin >> u >> v >> w;
// scanf("%d %d %d", &u, &v, &w);
}
s = 1;
dijkstra();
for (int i = 1; i <= n; i++)
printf("%d ", d[i]);
puts("");
return 0;
}


### 最小生成树（Kruskal）

#include<bits/stdc++.h>
using namespace std;
struct rec { int x, y, z; }edge[500010];
int fa[100010], n, m, ans;

bool cmp(rec a, rec b) {
return a.z < b.z;
}
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }

int main() {
//并查集初始化
for (int i = 1; i <= n; ++i)fa[i] = i;
cin >> n >> m;
for (int i = 1; i <= n; ++i)
cin >> edge[i].x >> edge[i].y >> edge[i].z;
//按照边权排序
sort(edge + 1, edge + 1 + n, cmp);
//求最小生成树
for (int i = 1; i <= m; ++i) {
int x = find(edge[i].x);
int y = find(edge[i].y);
if (x == y) continue;
fa[x] = y; //如果不在同一个集合,总权值更新,并且集合和并
ans += edge[i].z;
}
cout << ans << endl;
}


### 最小生成树（Prim）

const int inf=2147483647;
int a[manx][manx], d[manx], n, m, ans;
bool vis[manx];
void prim(){
for(int i=1;i<=n;i++) d[i]=inf,vis[i]=0,a[i][i]=0; //初始化各数组
d[1]=0; //1作为起点
for(int i=1;i<n;i++) //重复n-1次
{
int x=0;
for(int j=1;j<=n;j++)
if( !vis[j] && (x==0||d[j]<d[x]) ) //寻找未访问过且离集合S最近的点
x=j;
vis[x]=1;
for(int y=1;y<=n;y++)  //第一轮d[y]中由起点可到达的点得到更新
d[y]=min( d[y ], a[x][y]) //这里与Dij不同，因为Dij求的是两点间的距离，而这里是点到集合距离
}
}


### 强连通分量（korasaju 算法）

int V;                     // 顶点数
vector<int> G[maxn];       // 图的邻接表表示
vector<int> rG[maxn];      // 反向图
vector<int> vs;            // 后序遍历顺序的顶点列表
bool book[maxn];           // 访问标记
int cmp[maxn];             // 所属强连通分量的拓补序
void dfs(const int& v) {
book[v] = true;
for (int i = 0; i < G[v].size(); ++i)
if (!book[G[v][i]])dfs(G[v][i]);
vs.push_back(v);
}
void rdfs(const int& v, const int& k) {
book[v] = true; cmp[v] = k;
for (int i = 0; i < rG[v].size(); ++i)
if (!book[rG[v][i]])rdfs(rG[v][i], k);
}
//korasaju 算法核心：双重DFS标记
int scc() {
ms(book, false); vs.clear();
for (int v = 0; v < V; ++v)
if (!book[v])dfs(v);
ms(book, false); int k = 0;
for (int i = vs.size() - 1; i >= 0; --i)
if (!book[vs[i]])rdfs(vs[i], k++);
return k;//k为连通分量的个数
}


### 强连通分量（Tarjan 算法）




### 线段树

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 5e5 + 10;
struct Node {
int l, r, val;
int sum;
int lmax, rmax, dat;
//不能简单的对比对比左右子区间的dat和值(区间最大和值)来更新本节点的区间最大和值，还要对比右子树的rmax+左子树的lmax的和值
}node[maxn << 2];
int n, m;
int a[maxn];

void pushdown(int rt) {
node[rt].sum = node[rt << 1].sum + node[rt << 1 | 1].sum;
node[rt].lmax = max(node[rt << 1].lmax, node[rt << 1].sum + node[rt << 1 | 1].lmax);
node[rt].rmax = max(node[rt << 1 | 1].rmax, node[rt << 1 | 1].sum + node[rt << 1].rmax);
node[rt].dat = max(node[rt << 1].dat, node[rt << 1 | 1].dat);
node[rt].dat = max(node[rt].dat, node[rt << 1].rmax + node[rt << 1 | 1].lmax);
}

//构建线段树，完成区间和，区间max，dat的更新
void build(int rt, int l, int r) {
node[rt].l = l, node[rt].r = r;
if (l == r) {
node[rt].val = node[rt].lmax = node[rt].rmax = node[rt].dat = a[l];
node[rt].sum = a[l];
return;
}
int mid = (l + r) >> 1;
build(rt << 1, l, mid); build(rt << 1 | 1, mid + 1, r);
pushdown(rt);
return;
}

Node query(int rt, int l, int r) {
if (l <= node[rt].l && node[rt].r <= r) {
return node[rt];
}
Node t1, t2, ans;
t1.dat = t1.sum = t1.lmax = t1.rmax = t2.dat = t2.sum = t2.lmax = t2.rmax = -inf;
int mid = (node[rt].l + node[rt].r) >> 1;

if (l <= mid) t1 = query(rt << 1, l, r), ans.sum += t1.sum;
if (r > mid) t2 = query(rt << 1 | 1, l, r), ans.sum += t2.sum;

ans.dat = max(max(t1.dat, t2.dat), t1.rmax + t2.lmax);
ans.lmax = max(t1.lmax, t1.sum + t2.lmax);
ans.rmax = max(t2.rmax, t2.sum + t1.rmax);
if (l > mid) ans.lmax = max(ans.lmax, t2.lmax);
if (r <= mid) ans.rmax = max(ans.rmax, t1.rmax);
return ans;
}

void update(int rt, int pos, int val) {
if (node[rt].l == pos && node[rt].l == node[rt].r) {
node[rt].sum = node[rt].val = val;
node[rt].lmax = node[rt].rmax = node[rt].dat = val;
return;
}
int mid = (node[rt].l + node[rt].r) / 2;
//cout<<pos<<" "<<rt<<" "<<mid<<endl;
if (pos <= mid) update(rt << 1, pos, val);
else update(rt << 1 | 1, pos, val);
pushdown(rt);
}

int main() {
//freopen("in.txt","r",stdin);
ios::sync_with_stdio(false);
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
build(1, 1, n);
int op, l, r;
while (m--) {
cin >> op >> l >> r;
if (op == 1) {
if (l > r) swap(l, r);
cout << query(1, l, r).dat << endl;//根据需要输出不同的
}
else {
update(1, l, r);
}
}
return 0;
}


### 树状数组

int n;
int a[1005],tree[1005]; //原数组和树状数组

int lowbit(int x) return x&(-x); //取出x的最低位1

//修改
void updata(int i,int p){    //在i位置加上p
while(i <= n){
tree[i] += p;//更新受影响的tree[i]
i += lowbit(i);
}
}

//查询
int getsum(int i){//求区间1到i的和
int res = 0;
while(i > 0){
res += tree[i];
i -= lowbit(i);
}
return res;
}


### 二分图：匈牙利算法

int M, N;            //M, N分别表示左、右侧集合的元素数量
int Map[MAXM][MAXN]; //邻接矩阵存图
int p[MAXN];         //记录当前右侧元素所对应的左侧元素
bool vis[MAXN];      //记录右侧元素是否已被访问过
bool match(int i)
{
for (int j = 1; j <= N; ++j)
if (Map[i][j] && !vis[j]) //有边且未访问
{
vis[j] = true;                 //记录状态为访问过
if (p[j] == 0 || match(p[j])) //如果暂无匹配，或者原来匹配的左侧元素可以找到新的匹配
{
p[j] = i;    //当前左侧元素成为当前右侧元素的新匹配
return true; //返回匹配成功
}
}
return false; //循环结束，仍未找到匹配，返回匹配失败
}
int Hungarian()
{
int cnt = 0;
for (int i = 1; i <= M; ++i)
{
memset(vis, 0, sizeof(vis)); //重置vis数组
if (match(i))
cnt++;
}
return cnt;
}


### 背包问题

//01背包
//dp[i][j] = max(dp[i−1][j], dp[i−1][j−w[i]]+v[i]) // 前提：j >= w[i]
int dp[max_m + 1],v[max_n + 1],w[max_n + 1];
memset(dp,0xcf,sizeof dp); //-inf
dp[0] = 1;
for(int i = 1;i <= n;++i)
for(int j = m;j >= v[i];--j) //倒序循环
dp[j] = max(dp[j],dp[j - v[i]] + w[i]);
int ans = 0;
for(int j = 0;j <= m;++j)
ans = max(ans,dp[j]);

//=================================//
//完全背包
//不装入第 i种物品，即dp[i−1][j]，同01背包；
//只有当装入第 i 个物品时（由于物品无限个）所以此时不应该转移到dp[i−1][j−w[i]]而应该转移到dp[i][j−w[i]]
//dp[i][j] = max(dp[i−1][j], dp[i][j−w[i]]+v[i]) // 前提：j >= w[i]
int dp[max_m + 1],v[max_n + 1],w[max_n + 1];
memset(dp,0xcf,sizeof dp);// -inf
dp[0] = 1;
for(int i = 1; i <= n; ++i)
for(int j = v[i]; j <= m; ++j)//正序循环
dp[j] = max(dp[j] ,dp[j - v[i]] + w[i]);
int ans = 0;
for(int j = 0;j <= m;++j)
ans = max(ans,dp[j]);

//=================================//
//多重背包



### 数位DP

LL dp[20][][];
//维数视情况决定，比如在【不要62】中，dp状态要区分有没有6
int a[20];
//给出数字范围在10^18以内可用，位数更多就扩大一下

LL ans = 0;
if (pos == -1) return 1;
if (!limit && !lead && dp[pos] != -1) return dp[pos];
//无前导零和最高位限制才能使用已储存的状态
int end = limit ? a[pos] : 9;
for (int i = 0; i <= end; i++) {
if (不合法的情况（除0以外）) continue;
//当前位为是零但不是前导零，也不合法，跳过（如果0不合法的话）
ans += dfs(pos - 1, lead&&(!i), limit && i == end);
//当前位是合法情况，继续搜
}
//没有前导零也没有最高位限制，说明本次统计结果是通用的
return ans;
}

LL part(LL x){
int len = 0; LL k = x;
while (k) { a[len++] = k % 10; k /= 10; }
memset(dp, -1, sizeof (dp));
return dfs(len - 1, true, true);
//如果涉及到前导零，一般刚开始时都是默认有的
}

//注意l=0的时候不能用，要特判
LL result(LL l , LL r) {
return part(r) - part(l-1);
}


### 网络流：Edmonds-karp增广路

const int inf = 0x3f3f3f3f,N = 2010, M = 20010;
int head[N], ver[M], edge[M], Next[M], v[N], incf[N], pre[N];
int n, m, s, t, tot, maxflow;

void add(int x, int y, int z) {
ver[++tot] = y, edge[tot] = z, Next[tot] = head[x], head[x] = tot;
ver[++tot] = x, edge[tot] = 0, Next[tot] = head[y], head[y] = tot;
}

bool bfs() {
memset(v, 0, sizeof v);
queue<int>q;
q.push(s), v[s] = 1;//压入源点
incf[s] = inf;//增广路上各边的最小剩余容量
while (q.size()) {
int x = q.front(); q.pop();
for (int i = head[x]; i; i = Next[i]) {
if (edge[i]) {
int y = ver[i];
if (v[y])continue;
incf[y] = min(incf[y], edge[y]);//更新最小容量
pre[y] = i;//保存前驱，便于找到最长路的具体方案
q.push(y), v[y] = 1;
if (y == t)return true;//找到汇点直接返回true
}
}
}
return false;
}

void update() {
int x = t;
while (x != s) {
int i = pre[x];
edge[i] -= incf[t];
edge[i ^ 1] += incf[t];//利用“成对存储”
//num xor 1 , odd - 1 even + 1
x = ver[i ^ 1];
}
maxflow += incf[t];
}

int main() {
freopen("in.txt", "r", stdin);
while (cin >> n >> m) {
//源点、汇点、边数、最大流
s = 1, t = n, tot = 1, maxflow = 0;
for (int i = 1; i <= m; ++i) {
int x, y, c; cin >> x >> y >> c;
}
//Edmonds-karp增广路算法的时间复杂度为 O(nm^2)
//然而实际运用中则远远达不到这个上界，效率较高。
//一般能处理10^3 ~ 10^4 规模的网络
while (bfs()) update();//BFS搜索增广路
cout << maxflow << endl;
}
}


### 网络流：Dinic算法

const int inf = 0x3f3f3f3f, N = 2010, M = 20010;
//d[N] 当前弧优化
int head[N], ver[M], edge[M], Next[M], d[N], now[M];
int n, m, s, t, tot, maxflow;
queue<int>q;

void add(int x, int y, int z) {
ver[++tot] = y, edge[tot] = z, Next[tot] = head[x], head[x] = tot;
ver[++tot] = x, edge[tot] = 0, Next[tot] = head[y], head[y] = tot;
}

bool bfs() {//残量网络上构建分层图
memset(d, 0, sizeof d);
while (q.size())q.pop();
q.push(s); d[s] = 1; now[s] = head[s];
while (q.size()) {
int x = q.front(); q.pop();
for (int i = head[x]; i; i = Next[i]) {
if (edge[i] && !d[ver[i]]) {
q.push(ver[i]);
d[ver[i]] = d[x] + 1;
if (ver[i == t])return true;
}
}
}
return false;
}

int dinic(int x, int flow) {//在当前分层图上增广
if (x == t)return flow;
int rest = flow, k, i;
for (i = now[x]; i && rest; i = Next[i]) {
if (edge[i] && d[ver[i]] == d[x] + 1) {
//int y = ver[i];
k = dinic(ver[i], min(rest, edge[i]));//递归
if (!k) d[ver[i]] = 0;//剪枝，去掉增广完毕的店
edge[i] -= k;
edge[i ^ 1] += k;
rest -= k;
}
}
now[x] = i;//当前弧优化（避免重复遍历从x出发不可拓展的边）
return flow - rest;
}

int main() {
freopen("in.txt", "r", stdin);
cin >> n >> m;
cin >> s >> t;
tot = 1;
for (int i = 1; i <= m; ++i) {
int x, y, c; cin >> x >> y >> c;
}
int flow = 0;
while (bfs())
while (flow = dinic(s, inf))maxflow += flow;
cout << maxflow << endl;
}



#### 另一种写法

const int N = 110, M = 20010, inf = 1e8;
int h[N], to[M], nxt[M], c[M], tot = 1;
int n, m, s, t;
int d[N], now[N];
queue<int> q;

inline void add(int a, int b, int w) {
to[++tot] = b;
c[tot] = w;
nxt[tot] = h[a];
h[a] = tot;
}

bool bfs() {
memset(d, 0, sizeof d);
d[s] = 1;
now[s] = h[s];
while (!q.empty()) q.pop();
q.push(s);
while (!q.empty()) {
int x = q.front();
q.pop();
for (int i = h[x], v; v = to[i], i; i = nxt[i]) {
if (c[i] && !d[v]) {
d[v] = d[x] + 1;
now[v] = h[v];
if (v == t)
return true;
q.push(v);
}
}
}
return false;
}
int find(int x, int flow) {
if (x == t)
return flow;
int rest = flow, k = 0;
for (int i = now[x], v; v = to[i], i && rest; i = nxt[i]) {
if (c[i] && d[v] == d[x] + 1) {
now[x] = i;
k = find(v, min(rest, c[i]));
if (!k)
d[v] = 0;
c[i] -= k;
c[i ^ 1] += k;
rest -= k;
}
}
return flow - rest;
}

int dinic(int s, int t) {
int maxflow = 0, flow = 0;
while (bfs())
while (flow = find(s, inf)) maxflow += flow;
return maxflow;
}

int main() {
cin >> n >> m;
int u, v;
while (cin >> u >> v) {
if (u == -1 && v == -1)
break;
}
s = 0, t = n + 1;
for (int i = 1; i <= m; i++) add(s, i, 1), add(i, s, 0);
for (int i = m + 1; i <= n; i++) add(i, t, 1), add(t, i, 0);
cout << dinic(s, t) << endl;
return 0;
}


### LCA（倍增）

int num = 0;

struct Edge {
int next, to;
}edges[2 * maxn];

void add_edge(int u, int v) {
num++;
edges[num].to = v;
}

void dfs(int u, int u_father) {
depth[u] = depth[u_father] + 1;
fathers[u][0] = u_father;
for (int i = 1; (1 << i) <= depth[u]; i++)
fathers[u][i] = fathers[fathers[u][i - 1]][i - 1];
for (int i = head[u]; i; i = edges[i].next) {
if (edges[i].to != u_father) dfs(edges[i].to, u);
}
}

int LCA(int u, int v) {
if (depth[u] < depth[v]) swap(u, v);
while (depth[u] > depth[v])
u = fathers[u][lg[depth[u] - depth[v]] - 1];
if (u == v) return u;
for (int k = lg[depth[u]] - 1; k >= 0; k--) {
if (fathers[u][k] != fathers[v][k]) {
u = fathers[u][k];
v = fathers[v][k];
}
}
return fathers[u][0];
}

int main(){
dfs(1, 0);
//常数优化
for (int i = 1; i <= n; i++) {
lg[i] = lg[i - 1] + (1 << lg[i - 1] == i);
}
}


### LCA（树剖）

int num = 0;
//top[i]：节点i所在重链的链头

struct Edge {
int next, to;
}edges[2 * maxn];

void add_edge(int u, int v) {
num++;
edges[num].to = v;
}

void getDepth(int now) {
siz[now] = 1;
for (int i = head[now]; i != 0; i = edges[i].next) {
int v = edges[i].to;
getDepth(v);
siz[now] += siz[v];
}
}
}

int t = 0;
if (!top[x]) top[x] = x;
for (int i = head[x]; i != 0; i = edges[i].next) {
int v = edges[i].to;
if (v != dad[x] && siz[v] > siz[t]) t = v;
}
if (t) {
top[t] = top[x];
}
for (int i = head[x]; i != 0; i = edges[i].next) {
int v = edges[i].to;
}
}

int LCA(int u, int v) {
while (top[u] != top[v]) {
if (depth[top[u]] < depth[top[v]]) swap(u, v);
}
//直到u和v位于同一条重链上
return (depth[u] < depth[v]) ? u : v;
//深度更小的那一个就是公共祖先
}

int main()
{
//	ios::sync_with_stdio(false);
//	int t; cin >> t; while (t--) {
int n, m;
scanf("%d", &n);
for (int i = 1; i <= n - 1; i++) {
int u, v;
scanf("%d%d", &u, &v);
}
getDepth(1);
return 0;
}


### 康托展开&逆康托展开

【原理】

$X = A[0] * (n-1)! + A[1] * (n-2)! + … + A[n-1] * 0!\\ （A[i]表示在位置i后比位置i上数小的数的个数）$

【举例】

$在 (1, 2, 3, 4, 5) 5个数的排列组合中，计算 (3, 4, 1, 5, 2) 的康托展开值\\ X = 2 * 4! + 2 * 3! + 0 * 2! + 1 * 1! + 0 * 0! = 61$

const int FAC[] = { 1,1,2,6,24,120,720,5040,40320,362880,3628800 };

int cantor(int* a) {//算出全排列对应的哈希值
int x = 0;
for (int i = 0; i < 9; i++) {
int smaller = 0;
for (int j = i + 1; j < 9; j++) {
if (a[j] < a[i]) smaller++;
}
x += FAC[9 - i - 1] * smaller;
}
return x+1;
//注意全排列数组a是从零开始的
}

void decantor(int x,int*ans) {//x哈希值，n数字个数，a算出的全排列
x--;
vector<int> v;
for (int i = 1; i <= 9; i++) v.push_back(i);
for (int i = 0; i < 9; i++) {
int r;
r = x / FAC[9 - i - 1];
x = x % FAC[9 - i - 1];
sort(v.begin(), v.end());
ans[i] = v[r];
v.erase(v.begin() + r);
}
//注意算出的全排列数组ans是从0开始的
}


### Miller-Rabin 素数检验算法

typedef long long ll;
typedef unsigned long long ull;
typedef long double lb;
inline ll ksc(ull x, ull y, ll p) {  // O(1)快速乘（防爆long long）
return (x * y - (ull)((lb)x / p * y) * p + p) % p;
}

inline ll ksm(ll x, ll y, ll p) {  //快速幂
ll res = 1;
while (y) {
if (y & 1) res = ksc(res, x, p);
x = ksc(x, x, p);
y >>= 1;
}
return res;
}

inline bool mr(ll x, ll p) {
if (ksm(x, p - 1, p) != 1) return 0;  //费马小定理
ll y = p - 1, z;
while (!(y & 1)) {  //一定要是能化成平方的形式
y >>= 1;
z = ksm(x, y, p);                    //计算
if (z != 1 && z != p - 1) return 0;  //不是质数
if (z == p - 1) return 1;  //一定要为1，才能继续二次探测
}
return 1;
}

inline bool prime(ll x) {
if (x < 2) return 0;
if (x == 2 || x == 3 || x == 5 || x == 7 || x == 43) return 1;
return mr(2, x) && mr(3, x) && mr(5, x) && mr(7, x) && mr(43, x);
}


### 树的直径

#### DFS版

#include<bits/stdc++.h>
using namespace std;

const int maxn = 1e5 + 5;
int vis[maxn], book[maxn];
vector<int> g[maxn];
int node = 1;
int path[maxn];
int n, m;
int ans = 0;

void init() {
for (int i = 1; i <= n; i++) {
path[i] = -1;
vis[i] = 0;
}
}

void dfs(int x, int dis) {
vis[x] = 1;
for (int i = 0; i < g[x].size(); i++) {
int vv = g[x][i];
if (!vis[vv]) {
path[vv] = x;
if (dis + 1 > ans) {
node = vv;
ans = dis + 1;
}
dfs(vv, dis + 1);
}
}
vis[x] = 0;
}

int main() {
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1, 0);
int s1 = node;
ans = 0;
init();
dfs(node, 0);
int s2 = node;
int maxDist = ans;
cout << s1 << " " << s2 << " " << maxDist;
return 0;
}


#### BFS版

#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;

struct edge {
int v, w;
edge(int v, int w) {
this->v = v;
this->w = w;
}
};
vector<edge> vec[10001];
int d[10001], ans;
bool vis[10001];
int node; // 记录第一次dfs最远的点
void bfs(int u) {
queue<int> q;
q.push(u);
while (!q.empty()) {
int x = q.front();
vis[x] = 1;
q.pop();
for (int i = 0; i < (int)vec[x].size(); i++) {
int y = vec[x][i].v;
if (vis[y]) continue;
d[y] = d[x] + vec[x][i].w;
if (d[y] > ans) {
ans = d[y];
node = y;
}
q.push(y);
}
}
}
int main() {
// freopen("test.txt","r",stdin);
int u, v, w;
while (scanf("%d%d%d", &u, &v, &w) == 3) {
vec[u].push_back(edge(v, w));
vec[v].push_back(edge(u, w));
}
memset(vis, 0, sizeof(vis));
ans = 0;
d[1] = 0;
bfs(1);
memset(vis, 0, sizeof(vis));
ans = 0;
d[node] = 0;
bfs(node);
printf("%d\n", ans);
return 0;
}

posted @ 2020-07-25 19:45  RioTian  阅读(325)  评论(11编辑  收藏