【模版】
算法
差分

差分

#include <iostream>
#include <map>
#define int long long
using namespace std;
int n;
int a[1000005];
map<int, int> mp;
signed main()
{
	cin >> n;
	for(int i = 1; i <= n; i++){
		int x, y;
		cin >> x >> y;
		mp[x]++, mp[y + 1]--;
	}
	int last = -1, x = 0;
	for(map<int, int>::iterator it = mp.begin(); it != mp.end(); it++){
		if(last != -1){
			int now = it -> first;
			a[x] += now - last;
		}
		last = it -> first;
		x += it -> second;
	}
	for(int i = 1; i <= n; i++) cout << a[i] << " ";
	return 0;
}
深搜

dfsdfs

inline bool check(int x, int y)
{
	if(x > n || y > m || x < 1 || y < 1) return 0;
	if(vis[x][y]) return 0;
	return 1;
}
inline void dfs(int x, int y, int ans)
{
	if(x == n && y == m){
		maxn = max(ans, maxn);
		return;
	}
	vis[x][y] = 1;
	for(int i = 0; i < 3; i++){
		int nx = dx[i] + x;
		int ny = dy[i] + y;
		if(check(nx, ny)){
			dfs(nx, ny, ans + a[nx][ny]);
			vis[nx][ny] = 0;
		}
	}
}
数论
【模版】快速幂

快速幂

#include <iostream>//cin cout 
using namespace std;//命名空间
long long n, m, k;
long long ksm(long long n, long long m, long long k)
{
	long long ans = 1;
	while(m){
		if(m & 1) ans = ((ans % k) * (n % k)) % k;//同余定理
		n = ((n % k) * (n % k)) % k;
		m >>= 1;
	}
	return ans;
}
int main()//主函数
{
	cin >> n >> m >> k;
	cout << ksm(n, m, k);
	return 0;//华丽结束
}
动态规划
【模版】最长不下降子序列(LIS)

最长不下降子序列 LISLIS

O(nlogn)O(nlogn)

#include <iostream>//cin cout
#include <algorithm>//upper_bound 
#include <cstring>//memset
using namespace std;//命名空间
int a[5005], dp[5005];
int main()//主函数
{
	int n;
	cin >> n;
	for(int i = 1; i <= n; i++) cin >> a[i];
	memset(dp, 0x3f3f3f3f, sizeof(dp));
	int maxn = dp[1];
	for(int i = 1; i <= n; i++){
		a[i] = *upper_bound(dp + 1, dp + n + 1, a[i]);	
	}
	int ans = 0;
	while(dp[ans + 1] != maxn) ans++;
	cout << ans;
	return 0;//华丽结束
}
【模版】换根dp

换根 dp\texttt{dp}

#include <iostream>
#include <vector>
#define int long long
using namespace std;
const int N = 1e6 + 5; 
vector<int> g[N];
int sz[N], f[N], dep[N], n;
inline void dfs(int u, int fa)
{
	sz[u] = 1;//记录大小 
	dep[u] = dep[fa] + 1;//记录深度 
	for(int i = 0; i < g[u].size(); i++){
		int v = g[u][i];
		if(v == fa) continue;
		dfs(v, u);//往下深搜 
		sz[u] += sz[v];//加上儿子的大小 
}
inline void dp(int u, int fa)
{
	for(int i = 0; i < g[u].size(); i++){
		int v = g[u][i];
		if(v == fa) continue;
		f[v] = f[u] - sz[v] * 2 + n;//dp 
		dp(v, u);//继续往下dp 
	}
}
signed main()
{
	cin >> n;
	for(int i = 1; i < n; i++){
		int u, v;
		cin >> u >> v;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	dfs(1, 1);
	for(int i = 1; i <= n; i++) f[1] += dep[i];
	dp(1, 1);
	int ans = -1;
	int id;
	for(int i = 1; i <= n; i++){//统计答案 
		if(f[i] > ans){//如果大于ans,就记录 
			ans = f[i];
			id = i;//编号 
		}
	}
	cout << id;//输出 
	return 0;//华丽结束 
}
【模版】st表

stst

#include <iostream>//cin cout
#include <cmath> 
using namespace std;//命名空间
int x, y;
const int N = 100005;
int a[N];
int st[N][35], log1[N];
inline int query(int x, int y)
{
	int k = log1[y - x + 1];
	return max(st[x][k], st[y - (1 << k) + 1][k]);
}
int main()//主函数
{
	int n, m;
	scanf("%d%d", &n, &m);
	log1[0] = -1;
	for(int i = 1; i <= n; i++){
		scanf("%d", &a[i]);
		st[i][0] = a[i];
		log1[i] = log1[i >> 1] + 1;
	}
	for(int j = 1; j <= log1[n]; 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]);
		}
	}
	for(int i = 1; i <= m; i++){
		scanf("%d%d", &x, &y);
		printf("%d\n", query(x, y));
	}
	return 0;//华丽结束
}
数据结构
【模版】单链表

单链表

#include <bits/stdc++.h>
using namespace std;
struct node{
	int data;//存储数据 
	node *next;//存下一个数据点的地址 
};
node *p, *head = NULL;
inline void create_list()//构建单链表 
{
	int n, x;
	cin >> n;
	for(int i = 1; i <= n; i++){
		cin >> x;
		p = new node;//申请新的空间
		p -> data = x;//将 x 封装成 node 节点
		//反向插入写法,从头部插入
		p -> next = head;//next 指向此时 head 所指向的地址
		head = p;//将 head 指向自己
	}
}
inline void print_list()
{
	p = head;
	while(p != NULL){
		cout << p -> data << " ";
		p = p -> next;//让 p 跳到下一个节点 
	}
	cout << "\n";
} 
inline void insert_list(int k, int x)//在第 k 个节点插入 x
{
	p = new node;
	p -> data = x;
	if(k == 1){//头部插入
		p -> next = head;//next 指向此时 head 指向的节点,即目前第一个节点
		head = p;//head 指向自己,称谓新的首节点
	}
	else{//非头部插入
		int i = 1;
		node *q = head;
		while(i < k - 1){//找到第 k-1 个节点
			q = q -> next;
			i++;
		}
		p -> next = q -> next;
		//q 为第 k-1 个节点,它的 next 指向此时第 k 个节点,将此值赋予 p 的 next 后,p 将成为新的第 k 个节点 
		q -> next = p;;//第 k-1 个节点的 next 指向 p,即指向了新的第 k 个节点
	}
} 
inline void delete_list(int k)
{
	if(k == 1){//头部删除 
		p = head;
		head = p -> next;
		delete p;
	}
	else{
		int i = 1; 
		p = head;
		while(i < k - 1){//p 为第 k - 1 个节点 
			p = p -> next;
			i++;
		}
		node *q = p -> next;//q 为待删除的第 k 个节点 
		p -> next = q -> next;//p 指向第 k + 1 节点 
		delete q;
	}	
}
int main()
{

	return 0;
}
【模版】单调栈

单调栈

#include <iostream>
using namespace std;
const int N = 3e6 + 5;
int h[N], res[N];
struct stack{//手写栈
    int a[N];
    int t = 0;
    inline void push(int x){a[++t] = x;}
    inline int top(){return a[t];}
    inline void pop(){t--;}
    inline int empty(){return t == 0 ? 1 : 0;}
}st;
int main()
{
    int n;
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> h[i];
    for(int i = n; i >= 1; i--){
        while(!st.empty() && h[st.top()] <= h[i]) st.pop();
        st.empty() ? res[i] = 0 : res[i] = st.top();
        st.push(i);
    }
    for(int i = 1; i <= n; i++) cout << res[i] << " ";
    return 0;
}
图论
【模版】最短路

最短路

dijkstra

dijkstradijkstra

O(n^2)

O(n2)O(n^2)

#include <bits/stdc++.h>
using namespace std;
struct edge{
	int v, w;
};
vector<edge> g[10005];
int d[10005], vis[10005];
void dijkstra(int st)
{
	memset(d, 127, sizeof(d));
	d[st] = 0;//从起点出发
	for(int i = 1; i <= n; i++){
		int t = d[0], k;
		for(int j = 1; j <= n; j++){
			if(!vis[j] && d[j] < t){
				t = d[j];
				k = j;
			}
		}
		for(int j = 0; j < g[k].size(); j++){
			int val = g[k][j].v;
			int wal = g[k][j].w;
			if(d[val] > d[k] + wal) d[val] = d[k] + wal;
		}
		vis[k] = 1;
	}
}
int main()
{
	
	return 0;
}
O(nlogn)

O(nlogn)O(nlogn)

#include <iostream>
#include <queue>
#include <cstring>
#define int long long
using namespace std;
int n, m, st;
struct poi{
	int v, w;
	bool operator < (poi x) const{
		return w > x.w;
	}
};
const int N = 2e5 + 5;
int d[N], vis[N];
vector<poi> g[N];
priority_queue<poi> q;
void dijkstra(int st)
{
	memset(d, 0x3f, sizeof(d));
	d[st] = 0;
	q.push({st, 0});
	while(!q.empty()){
		int u = q.top().v;
		q.pop();
		if(vis[u]) continue;
		vis[u] = 1;
		for(int i = 0; i < g[u].size(); i++){
			int v = g[u][i].v, w = g[u][i].w;
			if(d[u] + w < d[v]){
				d[v] = d[u] + w;
				q.push({v, d[v]});
			} 
		}
	} 
}
signed main()
{
	cin >> n >> m >> st;
	for(int i = 1; i <= m; i++){
		int u, v, w;
		cin >> u >> v >> w;
		g[u].push_back((poi){v, w});
	}
	dijkstra(st);
	for(int i = 1; i <= n; i++) cout << d[i] << " ";
	return 0;
}
spfa

spfaspfa

O(n)O(nm)O(n)-O(nm)

#include <bits/stdc++.h>
using namespace std;
struct poi{
	int v, w;
};
int n, m, s;
queue<int> q;
bool vis[100005];
long long d[100005];
long long cnt[100005];
vector<poi> g[100005];
void spfa(int st)
{
	memset(d, 127, sizeof(d));
	memset(vis, 0, sizeof(vis));
	d[st] = 0;
	vis[st] = 1;
	q.push(st);
	while(!q.empty()){
		int k = q.front();
		q.pop();
		vis[k] = 0;
		for(int i = 0; i < g[k].size(); i++){
			int v = g[k][i].v;
			int w = g[k][i].w;
			if(d[k] + w < d[v]){
				d[v] = d[k] + w;
				if(!vis[v]){
					vis[v] = 1;
					q.push(v);
					cnt[v]++;
					if(cnt[k] > n) cout << -1, exit(0);//负环
				}
			}
		}
	}
}
int main()
{
	cin >> n >> m >> s;
	for(int i = 1; i <= m; i++){
		int a, b, c;
		cin >> a >> b >> c;
		g[a].push_back((poi){b, c});
	}
	spfa(s);
	cout << d[n];
	return 0;
}
floyd

floydfloyd

O(n3)O(n^3)

void floyd()
{
  	memset(f, 127, sizeof(f));
	for(int k = 1; k <= n; k++){
		for(int i = 1; i <= n; i++){
			f[i][i] = 0;
			for(int j = 1; j <= n; j++){
				f[i][j] = min(f[i][j], f[i][k] + f[k][j]);
			}
		}
	}			
}
【模版】最小生成树

MSTMST

kruskal

kruskalkruskal

#include <bits/stdc++.h>//万能头文件
using namespace std;//命名空间
int fa[1000001];
int n, m, res, cnt;
struct poi{
	int x, y, z;
}g[1000001];
bool cmp(poi a, poi b)
{
	return a.z < b.z;
}
int find(int x)//并查集 
{
	if(x == fa[x]) return x;
	return fa[x] = find(fa[x]);
}
int main()//主函数
{
	cin >> n >> m;
	for(int i = 1; i <= m; i++) cin >> g[i].x >> g[i].y >> g[i].z;
	stable_sort(g + 1, g + m + 1, cmp);//排序
	for(int i = 1; i <= n; i++) fa[i] = i;//并查集初始化 
	for(int i = 1; i <= m; i++){
		int x = find(g[i].x);//并查集 
		int y = find(g[i].y);//并查集 
		if(x == y) continue;//若出现两个点已经联通了,那么这一条边就不需要了
		fa[x] = y;//将y、x合并 
		res += g[i].z;//将边权计入答案
		cnt++;//边数++ 
	}
	if(cnt == n - 1) cout << res;
	else cout << "No MST";//没有最小生成树 
	return 0;//华丽结束
}
prim

primprim

O(n2)O(n^2)

#include <bits/stdc++.h>//万能头文件
using namespace std;//命名空间
bool vis[1001];
int n, m, res;
int a[1001][1001], d[1001];
void prim()
{
	memset(d, 0x3f, sizeof(d));
	memset(vis, 0, sizeof(vis));
	d[1] = 0;
	for(int i = 1; i <= n; i++){
		int x = 0;
		for(int j = 1; j <= n; j++){
			if(!vis[j] && d[j] < d[x]) x = j;
		}
		vis[x] = 1;
		for(int j = 1; j <= n; j++){
			if(!vis[j]) d[j] = min(d[j], a[x][j]);
		}
	}
}
int main()//主函数
{
	memset(a, 0x3f, sizeof(a));
	cin >> n >> m;
	for(int i = 1; i <= n; i++) a[i][i] = 0;
	for(int i = 1; i <= m; i++){
		int x, y, z;
		cin >> x >> y >> z;
		a[x][y] = a[y][x] = min(a[x][y], z);
	}
	prim();
	for(int i = 2; i <= n; i++) res += d[i];
	cout << res;
	return 0;//华丽结束
}
【模版】树状数组

树状数组

基础操作
#include <bits/stdc++.h>
using namespace std;
int n, m, l, r;
int a[100005], c[100005];
int lowbit(int x)
{
    return x & (-x);
}
int ask(int x)//区间[1,x]的前缀和
{
    int ans = 0;
    while(x > 0){
        ans += c[x];
        x -= lowbit(x);
    }
    return ans;
}
void add(int x, int d)//树状数组添加
{
    while(x <= n){
        c[x] += d;
        x += lowbit(x);
    }
}
int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i];
    for(int i = 1; i <= n; i++) add(i, a[i]);
    cin >> m;
    for(int i = 1; i <= m; i++){
        cin >> l >> r;
        cout << ask(r) - ask(l - 1) << "\n";
    }
    return 0;
}
离散化

离散化

所谓离散化就是指对序列中的数值按数值大小重新标号,最小值标为 11,最大值标为 nn

步骤:

①先建立一个备用数组 t[i]t[i];

②对数组 t[i]t[i] 排序;

③对数组 t[i]t[i] 去重;

④将原数值 a[i]a[i] 放到 t[i]t[i]二分查找,得到自己的顺序号,将顺序号更新为自己的新数值。

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i], t[i] = a[i];
    sort(t + 1, t + n + 1);//排序
    int len = unique(t + 1, t + n + 1) - t - 1;//去重
    for(int i = 1; i <= n; i++){
        a[i] = lower_bound(t + 1, t + len + 1, a[i]) - t;//将原数重新标号
    }
    return 0;
}
逆序对

逆序对

#include <bits/stdc++.h>
using namespace std;
int n, m, l, r;
long long ans = 0;
int a[100005], c[100005];
int lowbit(int x)
{
    return x & (-x);
}
int ask(int x)//区间[1,x]的前缀和
{
    int ans = 0;
    while(x > 0){
        ans += c[x];
        x -= lowbit(x);
    }
    return ans;
}
void add(int x, int d)//树状数组添加
{
    while(x <= n){
        c[x] += d;
        x += lowbit(x);
    }
}
int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i];
    for(int i = n; i >= 1; i--){
        ans += ask(a[i] - 1);//求出序列后方比a[i]小的数量
        add(a[i], 1);//将数字加入树状数组
    }
    cout << ans;
    return 0;
}

用离散化优化:

#include <bits/stdc++.h>
using namespace std;
int n, m, l, r;
long long ans = 0;
int a[100005], c[100005], t[100005];
int lowbit(int x)
{
    return x & (-x);
}
int ask(int x)//区间[1,x]的前缀和
{
    int ans = 0;
    while(x > 0){
        ans += c[x];
        x -= lowbit(x);
    }
    return ans;
}
void add(int x, int d)//树状数组添加
{
    while(x <= n){
        c[x] += d;
        x += lowbit(x);
    }
}
int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i], t[i] = a[i];
    //离散化
    sort(t + 1, t + n + 1);//排序
    int len = unique(t + 1, t + n + 1) - t - 1;//去重
    for(int i = 1; i <= n; i++){
        a[i] = lower_bound(t + 1, t + len + 1, a[i]) - t;//将原数重新标号
    }
    for(int i = n; i >= 1; i--){
        ans += ask(a[i] - 1);//求出序列后方比a[i]小的数量
        add(a[i], 1);//将数字加入树状数组
    }
    cout << ans;
    return 0;
}
【模版】线段树
线段树
#include <bits/stdc++.h>
using namespace std;
const int N = 1000005;
int n, m, p, x, y, a[N], sum[4 * N];
inline void pushup(int rt)
{
	sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
inline void build(int l, int r, int rt)//建树 
{
	if(l == r){ sum[rt] = a[l]; return;}
	int mid = (l + r) >> 1;
	build(l, mid, rt << 1);
	build(mid + 1, r, rt << 1 | 1);
	pushup(rt);
}
inline void update(int l, int r, int rt, int x, int d)//单点修改 
{
	if(l == r){ sum[rt] = d; return;}
	int mid = (l + r) >> 1;
	if(x <= mid) update(l, mid, rt << 1, x, d);
	else update(mid + 1, r, rt << 1 | 1, x, d);
	pushup(rt);
}
inline int query(int l, int r, int rt, int x, int y)//区间查询 
{
	if(x <= l && r <= y) return sum[rt];//区间[l,r]包含[x,y]
	int mid = (l + r) >> 1;
	int res = 0;
	if(x <= mid) res += query(l, mid, rt << 1, x, y);
	if(y > mid) res += query(mid + 1, r, rt << 1 | 1, x, y);
	return res;
}
int main()
{
    int m, n;
    cin >> m >> n;
    memset(a, 127, sizeof(a));
    for(int i = 1; i <= m; i++) cin >> a[i];
    build(1, m, 1);//建树 
    for(int i = 1; i <= n; i++){
    	cin >> p >> x >> y;
    	if(p == 1) cout << query(1, m, 1, x, y) << " ";
    	else update(1, m, 1, x, y);
	}
    return 0;
}

LazytagLazytag

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 1e5;
int n, m, q, l, r, x;
int a[N], add[N], sum[4 * N];
void pushup(int rt)
{
	sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
void pushdown(int rt, int l, int r)
{
	if(add[rt] == 0) return;
	add[rt << 1] += add[rt];
	add[rt << 1 | 1] += add[rt];
	sum[rt << 1] += add[rt] * l;
	sum[rt << 1 | 1] += add[rt] * r;
	add[rt] = 0;
}
void build(int l, int r, int rt)//建树 
{
	if(l == r){ sum[rt] = a[l]; return;}
	int mid = (l + r) >> 1;
	build(l, mid, rt << 1);
	build(mid + 1, r, rt << 1 | 1);
	pushup(rt);
}
void update(int l, int r, int rt, int x, int d)//单点修改 
{
	if(l == r){ sum[rt] = d; return;}
	int mid = (l + r) >> 1;
	if(x <= mid) update(l, mid, rt << 1, x, d);
	else update(mid + 1, r, rt << 1 | 1, x, d);
	pushup(rt);
}
void modify(int x, int y, int d, int l, int r, int rt)//区间修改[x,y]+d 
{
	if(x <= l && y >= r){
		sum[rt] += d * (r - l + 1);//当前的[l,r]都在[x,y]范围内 
		add[rt] += d;//标记
		return; 
	} 
	int mid = (l + r) >> 1;
	pushdown(rt, mid - l + 1, r - mid);
	if(x <= mid) modify(x, y, d, l, mid, rt << 1);
	if(y > mid)  modify(x, y, d, mid + 1, r, rt << 1 | 1);
	pushup(rt);
}
int query(int l, int r, int rt, int x, int y)//区间查询 
{
	if(x > r || y < l) return 0;
	if(x <= l && r <= y) return sum[rt];//区间[x,y]包含[l,r]
	int mid = (l + r) >> 1;
	pushdown(rt, mid - l + 1, r - mid);
	int ans = 0;
	if(x <= mid) ans += query(l, mid, rt << 1, x, y);
	if(y > mid) ans += query(mid + 1, r, rt << 1 | 1, x, y);
	return ans;
}
signed main()
{
    cin >> n >> m;
    for(int i = 1; i <= n; i++) cin >> a[i];
    build(1, n, 1);//建树 
    for(int i = 1; i <= m; i++){
    	cin >> q >> l >> r;
		if(q == 1){
			cin >> x;
			modify(l, r, x, 1, n, 1);
		}	 
		else cout << query(1, n, 1, l, r) << "\n";
	}
    return 0;
}
权值线段树
【模板】
#include <bits/stdc++.h>
using namespace std;
const int N = 1e7;
int n, m, q, l, r, x;
int a[N], sum[4 * N];
void pushup(int rt)
{
	sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
void build(int l, int r, int rt)//建树 
{
	if(l == r){ sum[rt] = 0; return;}
	int mid = (l + r) >> 1;
	build(l, mid, rt << 1);
	build(mid + 1, r, rt << 1 | 1);
	pushup(rt);
}
void update(int l, int r, int rt, int x, int d)//单点修改 
{
	if(l == r){ sum[rt] = d; return;}
	int mid = (l + r) >> 1;
	if(x <= mid) update(l, mid, rt << 1, x, d);
	else update(mid + 1, r, rt << 1 | 1, x, d);
	pushup(rt);
}
int query(int l, int r, int rt, int x, int y)//区间查询 
{
	if(x > r || y < l) return 0;
	if(x <= l && r <= y) return sum[rt];//区间[x,y]包含[l,r]
	int mid = (l + r) >> 1;
	int ans = 0;
	if(x <= mid) ans += query(l, mid, rt << 1, x, y);
	if(y > mid) ans += query(mid + 1, r, rt << 1 | 1, x, y);
	return ans;
}
int kth(int l, int r, int rt, int k)//求第k小的数 
{
	if(l == r) return r;//到达叶子结点
	int mid = (l + r) >> 1;
	int lsum = sum[rt << 1];
	if(k <= lsum) return kth(l, mid, rt << 1, k);
	return kth(mid + 1, r, rt << 1 | 1, k - lsum);
}
int pre(int x)//查找前驱 
{
	int k = query(1, N, 1, 1, x - 1);
	if(!k) return -1;//它是最小值,无前驱
	return kth(1, N, 1, k); 
} 
int next(int x, int mx)
{
	int k = query(1, N, 1, 1, x - 1) + 1;
	if(!(k ^ mx)) return -1;//它是最大值,无前驱
	return kth(1, N, 1, k); 
}
int main()
{

    return 0;
}
动态开点
#include <bits/stdc++.h>
using namespace std;
const int N = 1e7;
struct node{
    int lson, rson, sum;
}T[N * 3];
int n, opt, x, cnt = 1, root = 1;
void update(int rt, int l, int r, int k, int v)//更新数值 
{
    if(l == r){
        T[rt].sum += v;
        return;
    }
    int mid = (l + r) >> 1;
    if(k <= mid){
    	if(!T[rt].lson) T[rt].lson = ++cnt;
    	update(T[rt].lson, l, mid, k, v);
	}
    else{
    	if(!T[rt].rson) T[rt].rson = ++cnt; 
    	update(T[rt].rson, mid + 1, r, k, v);
	}
    T[rt].sum = T[T[rt].lson].sum + T[T[rt].rson].sum;//类似pushup 
}
int query(int l, int r, int rt, int x, int y)//区间求和
{ 
    if(rt == 0)return 0;
    if(x <= l && r <= y) return T[rt].sum; 
    int s = 0;
    int mid = (l + r) >> 1;
    if(x <= mid) s += query(T[rt].lson, l, mid, x, y);
    if(y > mid) s += query(T[rt].rson, mid + 1, r, x, y);
    return s;
}
int kth(int rt, int l, int r, int k)//找第K个数 
{
    if(l == r) return r;
    int mid = (l + r) >> 1;
    int lson = T[rt].lson;
    int rson = T[rt].rson;
    if(T[lson].sum >= k)return kth(lson, l, mid, k);//左子树总数量>=k,那么第k个肯定在左子树 
    return kth(rson, mid + 1, r, k - T[lson].sum);//第k个在右子树,递归右子树,注意k值的变化
}
int main()
{
	
	return 0;
}
【模板】树的重心与直径
重心
#include <bits/stdc++.h>
using namespace std;
int n;
const int N = 1e6 + 5;
vector<int> g[N];
int d[N], a[N], ans = 1e9, num;
inline void dfs(int u, int fa)
{
	d[u] = 1;
	int max_part = 0; 
	for(int i = 0; i < g[u].size(); i++){//求出节点数量 
		int v = g[u][i];//枚举儿子节点 
		if(v == fa) continue; 
		dfs(v, u); 
		d[u] += d[v];//加上儿子节点数量 
		max_part = max(max_part, d[v]);
	}
	max_part = max(max_part, n - d[u]);//比较除了u以外的连通块 
	if(max_part < ans){//如果更优 
		ans = max_part;//记录 
		num = 0;//更新 
		a[++num] = u;//记录 
	} 
	else if(max_part == ans) a[++num] = u;//记录 
}
int main()
{
	cin >> n;
	for(int i = 1; i < n; i++){
		int u, v;
		cin >> u >> v;
		g[u].push_back(v);
		g[v].push_back(u);
		//建树 
	}
	dfs(1, 0);
	sort(a + 1, a + num + 1);//排序 
	cout << num << "\n";//重心个数 
	for(int i = 1; i <= num; i++) cout << a[i] << " ";
	return 0;
}
直径

直径

权值树
#include <bits/stdc++.h>
using namespace std;
int n, m;
const int N = 1e6 + 5;
struct poi{
	int v, w;
};
int d[N];
vector<poi> g[N];
inline void dfs(int u, int fa, int len)
{
	d[u] = len;
	for(int i = 0; i < g[u].size(); i++){
		int v = g[u][i].v, w = g[u][i].w;
		if(v == fa) continue;
		dfs(v, u, len + w);
	}
}
int main() 
{
    cin >> n >> m;
	for(int i = 1; i <= m; i++){//建树 
		int u, v, w;
		cin >> u >> v >> w;
		g[u].push_back((poi){v, w});
		g[v].push_back((poi){u, w});
	}    
	dfs(1, -1, 0);//求最远距离 
	int s = 1;
	for(int i = 2; i <= n; i++){
		if(d[i] > d[s]) s = i;//求直径的一个端点 
	}
	dfs(s, -1, 0);//求最远距离
	int t = s;
	for(int i = 1; i <= n; i++){
		if(d[i] > d[t]) t = i;//求直径的另一个端点 
	}
	cout << d[t];//输出直径 
    return 0;
}
无权树
#include <bits/stdc++.h>
using namespace std;
int n, m;
const int N = 1e6 + 5;
int d[N];
vector<int> g[N];
inline void dfs(int u, int fa, int len)
{
	d[u] = len;
	for(int i = 0; i < g[u].size(); i++){
		int v = g[u][i];
		if(v == fa) continue;
		dfs(v, u, len + 1);
	}
}
int main() 
{
    cin >> n >> m;
	for(int i = 1; i <= m; i++){//建树 
		int u, v, w;
		cin >> u >> v;
		g[u].push_back(v);
		g[v].push_back(u);
	}    
	dfs(1, -1, 0);//求最远距离 
	int s = 1;
	for(int i = 2; i <= n; i++){
		if(d[i] > d[s]) s = i;//求直径的一个端点 
	}
	dfs(s, -1, 0);//求最远距离
	int t = s;
	for(int i = 1; i <= n; i++){
		if(d[i] > d[t]) t = i;//求直径的另一个端点 
	}
	cout << d[t];//输出直径 
    return 0;
}
【模版】树的独立集和支配集
独立集

独立集

#include <bits/stdc++.h>
using namespace std;
const int N = 5e4 + 5;
int n, d[N], dp[N][2];
vector<int> g[N];
inline void dfs(int u, int fa)
{
	dp[u][0] = 0;
	dp[u][1] = 1;//选点u 
	for(int i = 0; i < g[u].size(); i++){
		int v = g[u][i];
		if(v == fa) continue;
		dfs(v, u);
		dp[u][0] += max(dp[v][0], dp[v][1]);//不选点u,子节点可选可不选 
		dp[u][1] += dp[v][0];//选点u,那么子节点不能再选了 
	}
}
int main()
{
	cin >> n;
	for(int i = 1; i < n; i++){
		int a, b;
		cin >> a >> b;
		g[b].push_back(a);
		g[a].push_back(b);
	}
	dfs(1, -1);//从节点1开始dfs,1相当于根节点 
	cout << max(dp[1][1], dp[1][0]) << "\n";
	return 0;
}
支配集

支配集

#include <bits/stdc++.h>
#define int long long
using namespace std;
int n;
const int N = 1e5, inf = 99999999;
int dp[N][3], a[N];
vector<int> g[N];
inline void dfs(int u, int fa)//模板
{
	dp[u][2] = 0;//定义为不要点u加入支配集,点u为根的子树所有节点都被覆盖,但是点u不被任何一个子节点覆盖的情况下,支配集最少节点的个数 
	dp[u][1] = 1;//定义为选择点u加入支配集,并且点u为根的子树所有节点都被覆盖了的情况下,支配集中包含最少的节点个数 
	dp[u][0] = inf;//定义为不要点u加入支配集,点u为根的子树所有节点都被覆盖,并且点u被至少一个子节点覆盖的情况下,支配集最少节点个数
	int sum = 0, ans = inf;
	bool flag = 0;
	for(int i = 0; i < (int)g[u].size(); i++){
		int v = g[u][i];
		if(v == fa) continue;
		dfs(v, u);
		dp[u][1] += min(dp[v][1], min(dp[v][0], dp[v][2]));//点u被选,和子节点无关,累加子节点的三种情况的最小值,随便选 
		sum += min(dp[v][1], dp[v][0]);//累计子节点中已经被覆盖的情况,为dp[u][0]做准备
		dp[u][0] = sum;
		if(dp[v][0] >= dp[v][1]) flag = 1;//表示存在一个子节点已经被选择,可以通过他覆盖节点u
		if(dp[v][0] < dp[v][1]) ans = min(ans, dp[v][1] - dp[v][0]);//保存dp[v][1]和dp[v][0]的差值最小值 
        dp[u][2] += dp[v][0]; //为了得到最小值,我们把差值最小的从dp[v][0]替换为dp[v][1],强行选择这个子节点,这样点u就可以满足覆盖条件
	}	
	if(!flag && ans != inf) dp[u][0] += ans;
}
signed main()
{
	cin >> n;
	for(int i = 1; i <= n; i++){
		int id, k, m;
		cin >> id >> k >> m;
		a[id] = k;
		for(int j = 1; j <= m; j++){
            int r;
			cin >> r;
			g[id].push_back(r);
			g[r].push_back(id);
		}
	}
	dfs(1, -1);
	cout << min(dp[1][1], dp[1][0]);
	return 0;
}
【模版】图的强连通分量

TarjanTarjan

#include <iostream>
#include <vector>
using namespace std;
const int N = 5e5 + 5;
int vis[N], st[N];//st模拟栈,vis记录是否入栈 
int scc[N], sz[N], sc;//sc记录所在SCC的编号,scc记录在第几个SCC,sz记录大小 
int dfn[N], low[N];//dfn记录遍历次序,low记录祖先 
vector<int> g[N];
int n, m, dfncnt, top;
inline void tarjan(int u)
{
	dfn[u] = low[u] = ++dfncnt;
	st[++top] = u;//入栈
	vis[u] = 1;
	for(int i = 0; i < g[u].size(); i++){
		int v = g[u][i];
		if(!dfn[v]){//如果没被访问 
			tarjan(v);//继续往下搜索 
			low[u] = min(low[u], low[v]);
		}
		else if(vis[v]){
			low[u] = min(low[u], dfn[v]);
		}
	} 
	if(dfn[u] == low[u]){
		sc++;//记录编号 
		while(s[top] != u){//栈顶不等于u 
			scc[s[top]] = sc;//记录SCC 
			sz[sc]++;//第sc个SCC大小++ 
			st[s[top]] = 0;//出栈 
			top--;//出栈	
		}
		//将u记录到SCC中 
		scc[s[top]] = sc; 
		sz[sc]++;
		st[s[top]] = 0;
		top--;
	}
}
int main()
{
	cin >> n >> m;
	for(int i = 1; i <= m; i++){
		int u, v;
		cin >> u >> v;
		g[u].push_back(v);//有向图 
	}
	for(int i = 1; i <= n; i++){
		if(!dfn[i]) tarjan(i);//输出 
	}
	return 0;
}
【模版】图的割点和桥

割点

#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
const int N = 1e6 + 5;
vector<int> g[N];
int dfn[N], low[N];
int ans[N], vis[N];
int n, m, res, dfncnt, root;
inline void tarjan(int u, int fa)
{
	dfn[u] = low[u] = ++dfncnt;//时间戳 
	int child = 0;
	for(int i = 0; i < g[u].size(); i++){
		int v = g[u][i];
		if(!dfn[v]){
			child++;
			tarjan(v, u);
			low[u] = min(low[u], low[v]);
			if(dfn[u] <= low[v]){//是割点 
				if(child > 1 || u != root) ans[u] = 1;//标记 
			} 
		}
		else if(v != fa) low[u] = min(low[u], dfn[v]); 
	}
}
int main()
{
	cin >> n >> m;
	for(int i = 1; i <= m; i++){
		int x, y;
		cin >> x >> y;
		g[x].push_back(y);
		g[y].push_back(x);
	}
	for(int i = 1; i <= n; i++){
		if(!dfn[i]){
			root = i;
			tarjan(i, i);
		}
	}
	for(int i = 1; i <= n; i++){
		if(ans[i]) res++;
	}
	cout << res << "\n";
	for(int i = 1; i <= n; i++){
		if(ans[i]) cout << i << " ";
	}
	return 0;
}