1.并查集
并查集感觉还是比较easy的
const int maxn = 510;
int root[maxn];
int find(int x) {
if (root[x] == x) return x;
else root[x] = find(root[x]);
}
2.LCA
寻找一颗树里的的最近公共祖先,用倍增进行优化一下,数组开大,然后照着板子应该就可以了吧
#include<bits/stdc++.h>
using namespace std;
const int maxn =5e5+86;
vector<int>g[maxn];
int n, m, s;
int depth[maxn], dp[maxn][22];
void dfs(int u, int p, int d) {//先把深度给跑出来
dp[u][0] = p;//这个的上一个结点就是刚才的fa结点
depth[u] = d;//自上而下建立一颗树,初始的根结点先标记为d
int len = g[u].size();
for (int i = 0; i < len; i++) {
int v = g[u][i];
if (v == p) continue;
dfs(v, u, d + 1);
}
}
void init() {
for (int i = 1; i <= 20; i++)
for (int j = 1; j <= n; j++)
dp[j][i] = dp[dp[j][i - 1]][i - 1];
}
int lca(int x, int y) {
//cout << depth[x] << " " << depth[y] << endl;
if (depth[x] < depth[y]) swap(x, y);//交换一下
for (int i = log2(depth[x] - depth[y]); i >= 0; i--) {//向下取整
if ((1 << i) <= depth[x] - depth[y]) x = dp[x][i];
}//这样两边的差值就变成0了
if (x == y) return x;
for (int i = log2(depth[x]); i >= 0; i--) {
if (dp[x][i] != dp[y][i]) {
x = dp[x][i];
y = dp[y][i];
}
}
assert(x != y && dp[x][0] == dp[y][0]);
return dp[x][0];
}
int main() {
cin >> n >> m >> s;
for (int j = 1; j < n; j++) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(s, s, 0);
init();
for (int i = 1; i <= m; i++) {
int a, b;
cin >> a >> b;
cout << lca(a, b) << endl;
}
return 0;
}
3.素数筛
欧拉筛,能够处理1e8之间的数据,复杂度O(n);
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn = 1e8;
ll prime[50000000]; //就是个素数表
bool sf[maxn*2]; //判断这个数是不是素数,sf[i]中的i是从1到maxn的数
void sushu(){ //核心欧拉筛代码
ll num = 0; //num 用来记筛到第几个质数
memset(sf, true, sizeof(sf));
sf[1] = false;
sf[0] = false; //1 0 特判
for (int i = 2; i <= maxn; i++){ //外层枚举1~maxn
if (sf[i]) prime[++num] = i; //如果是质数就加入素数表
for (int j = 1; j <= num; j++){ //内层枚举num以内的质数
if (i * prime[j] > maxn) break; //筛完结束
sf[i * prime[j]] = false; //筛掉...
if (i % prime[j] == 0) break; //避免重复筛
}
}
}
int main() {
int n, q;
scanf("%d%d", &n, &q);
sushu();
while (q--) {
int a;
scanf("%d", &a);
printf("%d\n", prime[a]);
}
return 0;
}
4.最短路
dijkstra算法求单源最短路不能有负边权
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn =150005 ;
int n, m;
struct node {
ll to, dis;
};
vector<node>g[maxn];
struct edge {
ll id, dis;
bool operator<(const edge &rhs) const {
return dis > rhs.dis;
}
};
priority_queue<edge>qwq;
ll dis[maxn],vis[maxn];
void dijkstra(int s) {
for (int i = 1; i <= n; i++) dis[i] = 1e18;
dis[1] = 0;
memset(vis, 0, sizeof(vis));
while (!qwq.empty()) qwq.pop();
qwq.push({ s,0 });
while (!qwq.empty()) {
edge now = qwq.top();
qwq.pop();
if (vis[now.id] == 1) continue;
for (int i = 0; i < g[now.id].size(); i++) {
int to = g[now.id][i].to;
int dis2 = g[now.id][i].dis;
if (dis[to] > now.dis + dis2) {
dis[to] = now.dis + dis2;
//cout << to << " " << dis[to] << endl;
qwq.push({ to, dis[to] });
}
}
vis[now.id] = 1;
}
}
int main() {
cin >> n >> m;
while (m--) {
int x, y, z;
cin >> x >> y >> z;
g[x].push_back({ y,z });
}
dijkstra(1);
if (dis[n] == 1e18) cout << -1 << endl;
else cout << dis[n] << endl;
return 0;
}
再加一个spfa我猜最短路应该够用了吧
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn =150005 ;
int n, m;
struct node {
ll to, dis;
};
vector<node>g[maxn];
ll dis[maxn];bool vis[maxn];
void spfa() {
for (int i = 1; i <= n; i++) dis[i] = 1e18;
memset(vis, 0, sizeof(vis));
queue<int>qwq;
while (!qwq.empty()) qwq.pop();
qwq.push(1);
dis[1] = 0;
while (!qwq.empty()) {
int now = qwq.front();
vis[now] = 0;
qwq.pop();
for (int i = 0; i < g[now].size(); i++) {
int to = g[now][i].to;
int dis1 = g[now][i].dis;
if (dis[to] > dis1 + dis[now]) {
dis[to] = dis1 + dis[now];
if (!vis[to]) {
qwq.push({ to });
vis[to] = 1;
}
}
}
}
}
int main() {
cin >> n >> m;
while (m--) {
int x, y, z;
cin >> x >> y >> z;
g[x].push_back({ y,z });
}
spfa();
if (dis[n] == 1e18) cout << "impossible" << endl;
else cout << dis[n] << endl;
return 0;
}
好吧再加一个多源最短路
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 86, INF = 1e9;
int n, m, k;
int dis[maxn][maxn];
void floyd() {
for (int k = 1; k <= n; k++) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
}
}
}
}
int main() {
cin >> n >> m >> k;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (i == j) dis[i][j] = 0;
else dis[i][j] = INF;
}
}
while (m--) {
int u, v, w;
cin >> u >> v >> w;
dis[u][v] = min(dis[u][v], w);
}
floyd();
while (k--) {
int x, y;
cin >> x >> y;
if (dis[x][y] > INF / 2) cout << "impossible" << endl;
else cout << dis[x][y] << endl;
}
return 0;
}
5.DP
5.1 区间dp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn = 500 ;
int n;
int a[maxn];
int dp[maxn][maxn],sum[maxn];
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
sum[i] = sum[i - 1] + a[i];
}
for (int len = 2; len <= n; len++) {
for (int i = 1; i <= n; i++) {
int j = i + len - 1;
if (j > n) continue;
dp[i][j] = 1e8;
for (int k = i; k < j; k++) {
//cout << "**" << i << " " << k << " " << j << endl;
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + sum[j] - sum[i - 1]);
}
// cout <<i<<" "<<j<<" "<< dp[i][j] << endl;
}
}
cout << dp[1][n] << endl;
return 0;
}
5.2 树形dp
6.字符串哈希
将O(n)查询变成O(1)询问来加块查询速度
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int maxn = 1000010, base = 131;
char str[maxn];
ull h[maxn],p[maxn];
ull gethash(ull l,ull r) {
return h[r] - h[l - 1] * p[r - l + 1];
}
int main() {
scanf("%s", str + 1);
int n = strlen(str + 1);
p[0] = 1;
for (int i = 1; i <= n; i++) {
h[i] = h[i - 1] * base + str[i] - 'a' + 1;//计算前缀hash的值
p[i] = p[i - 1] * base;//计算131进制的位值
}
int m; cin >> m;
for (int i = 1; i <= m; i++) {
int l1, r1, l2, r2;
cin >> l1 >> r1 >> l2 >> r2;
//cout << gethash(l1, r1) << " " << gethash(l2, r2) << endl;
if (gethash(l1, r1) == gethash(l2, r2)) {
cout << "Yes" << endl;
}
else cout << "No" << endl;
}
return 0;
}