【复习】CSP2021-模板
一、dp
1. 01 背包
#include <iostream>
#include <cstdio>
using namespace std;
const int MAXM = 105;
const int MAXT = 1005;
int v[MAXM], w[MAXM];
int dp[MAXT];
int main()
{
int t, m;
scanf("%d%d", &t, &m);
for (int i = 1; i <= m; i++)
{
scanf("%d%d", v + i, w + i);
}
for (int i = 1; i <= m; i++)
{
for (int j = t; j >= v[i]; j--)
{
dp[j] = max(dp[j], dp[j - v[i]] + w[i]);
}
}
printf("%d\n", dp[t]);
return 0;
}
2. 完全背包
tips:开 long long
。
#include <iostream>
#include <cstdio>
#define int long long
using namespace std;
const int MAXM = 1e4 + 5;
const int MAXT = 1e7 + 5;
int v[MAXM], w[MAXM];
int dp[MAXT];
signed main()
{
int t, m;
scanf("%lld%lld", &t, &m);
for (int i = 1; i <= m; i++)
{
scanf("%lld%lld", v + i, w + i);
}
for (int i = 1; i <= m; i++)
{
for (int j = v[i]; j <= t; j++)
{
dp[j] = max(dp[j], dp[j - v[i]] + w[i]);
}
}
printf("%lld\n", dp[t]);
return 0;
}
3. 多重背包(二进制优化)
#include <iostream>
#include <cstdio>
using namespace std;
const int MAXN = 1e5 + 5;
const int MAXT = 4e4 + 5;
int v[MAXN], w[MAXN];
int dp[MAXT];
int main()
{
int n, t, tot = 0;
scanf("%d%d", &n, &t);
for (int i = 1; i <= n; i++)
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
for (int j = 1; j <= c; j <<= 1)
{
c -= j;
w[++tot] = a * j;
v[tot] = b * j;
}
if (c)
{
w[++tot] = a * c;
v[tot] = b * c;
}
}
for (int i = 1; i <= tot; i++)
{
for (int j = t; j >= v[i]; j--)
{
dp[j] = max(dp[j], dp[j - v[i]] + w[i]);
}
}
printf("%d\n", dp[t]);
return 0;
}
4. 区间 dp(环形类问题)
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 205;
int a[MAXN], sum[MAXN], dp1[MAXN][MAXN], dp2[MAXN][MAXN];
int main()
{
memset(dp1, 0x3f, sizeof(dp1));
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%d", a + i);
a[i + n] = a[i];
}
for (int i = 1; i <= (n << 1); i++)
{
sum[i] = sum[i - 1] + a[i];
dp1[i][i] = 0;
}
for (int len = 2; len <= n; len++)
{
for (int i = 1; i + len - 1 <= (n << 1); i++)
{
int j = i + len - 1;
dp1[i][j] = 0x3f3f3f3f;
for (int k = i; k < j; k++)
{
dp1[i][j] = min(dp1[i][j], dp1[i][k] + dp1[k + 1][j] + sum[j] - sum[i - 1]);
dp2[i][j] = max(dp2[i][j], dp2[i][k] + dp2[k + 1][j] + sum[j] - sum[i - 1]);
}
}
}
int ans1 = 0x3f3f3f3f, ans2 = 0;
for (int i = 1; i <= n; i++)
{
ans1 = min(ans1, dp1[i][i + n - 1]);
ans2 = max(ans2, dp2[i][i + n - 1]);
}
printf("%d\n%d\n", ans1, ans2);
return 0;
}
二、图论
1. 最短路
(1) dijkstra
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
const int MAXN = 1e5 + 5;
const int MAXM = 2e5 + 5;
int cnt;
int head[MAXN];
struct edge
{
int to, dis, nxt;
}e[MAXM];
void add(int u, int v, int w)
{
e[++cnt] = edge{v, w, head[u]};
head[u] = cnt;
}
struct que
{
int pos, dis;
bool operator <(const que &x)const
{
return x.dis < dis;
}
};
int dis[MAXN];
bool vis[MAXN];
priority_queue<que> pq;
void dijkstra(int s)
{
memset(dis, 0x3f, sizeof(dis));
dis[s] = 0;
pq.push(que{s, 0});
while (!pq.empty())
{
int u = pq.top().pos;
pq.pop();
if (vis[u])
{
continue;
}
vis[u] = true;
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to, w = e[i].dis;
if (dis[v] > dis[u] + w)
{
dis[v] = dis[u] + w;
pq.push(que{v, dis[v]});
}
}
}
}
int main()
{
int n, m, s;
scanf("%d%d%d", &n, &m, &s);
for (int i = 1; i <= m; i++)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
add(u, v, w);
}
dijkstra(s);
for (int i = 1; i <= n; i++)
{
printf("%d ", dis[i]);
}
return 0;
}
(2) spfa
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int MAXN = 1e4 + 5;
const int MAXM = 5e5 + 5;
int cnt;
int head[MAXN];
struct edge
{
int to, dis, nxt;
}e[MAXM];
void add(int u, int v, int w)
{
e[++cnt] = edge{v, w, head[u]};
head[u] = cnt;
}
int dis[MAXN];
bool vis[MAXN];
queue<int> q;
void spfa(int s)
{
memset(dis, 0x3f, sizeof(dis));
dis[s] = 0;
vis[s] = true;
q.push(s);
while (!q.empty())
{
int u = q.front();
q.pop();
vis[u] = false;
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to, w = e[i].dis;
if (dis[v] > dis[u] + w)
{
dis[v] = dis[u] + w;
if (!vis[v])
{
vis[v] = true;
q.push(v);
}
}
}
}
}
int main()
{
int n, m, s;
scanf("%d%d%d", &n, &m, &s);
for (int i = 1; i <= m; i++)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
add(u, v, w);
}
spfa(s);
for (int i = 1; i <= n; i++)
{
if (dis[i] == 0x3f3f3f3f)
{
printf("2147483647 ");
}
else
{
printf("%d ", dis[i]);
}
}
return 0;
}
(3) floyd
P2888 [USACO07NOV]Cow Hurdles S
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 305;
int n, m, t;
int dp[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++)
{
dp[i][j] = min(dp[i][j], max(dp[i][k], dp[k][j]));
}
}
}
}
int main()
{
memset(dp, 0x3f, sizeof(dp));
scanf("%d%d%d", &n, &m, &t);
for (int i = 1; i <= m; i++)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
dp[u][v] = min(dp[u][v], w);
}
floyd();
while (t--)
{
int a, b;
scanf("%d%d", &a, &b);
if (dp[a][b] == 0x3f3f3f3f)
{
puts("-1");
}
else
{
printf("%d\n", dp[a][b]);
}
}
return 0;
}
2. 负环
// measure it.
#include <cstdio>
#include <cstring>
#include <queue>
#define Debug(x) cout << #x << "=" << x << endl
typedef long long ll;
using namespace std;
const int MAXN = 2e3 + 5;
const int MAXM = 3e3 + 5;
int cnt;
int head[MAXN];
struct edge
{
int to, dis, nxt;
}e[MAXM << 1];
void Add(int u, int v, int w)
{
e[++cnt] = edge{v, w, head[u]};
head[u] = cnt;
}
int t, n, m;
int dis[MAXN], inq[MAXN];
bool vis[MAXN];
bool SPFA()
{
dis[1] = 0, inq[1] = 1;
queue<int> q;
q.push(1);
while (!q.empty())
{
int u = q.front();
q.pop();
vis[u] = false;
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to, w = e[i].dis;
if (dis[v] > dis[u] + w)
{
dis[v] = dis[u] + w;
if (!vis[v])
{
if (++inq[v] > n)
return false;
vis[v] = true;
q.push(v);
}
}
}
}
return true;
}
void Init()
{
cnt = 0;
memset(head, 0, sizeof(head));
memset(dis, 0x3f, sizeof(dis));
memset(vis, false, sizeof(vis));
memset(inq, 0, sizeof(inq));
}
int main()
{
scanf("%d", &t);
while (t--)
{
Init();
scanf("%d%d", &n, &m);
for (int i = 1, u, v, w; i <= m; i++)
{
scanf("%d%d%d", &u, &v, &w);
Add(u, v, w);
if (w >= 0)
Add(v, u, w);
}
if (SPFA())
puts("NO");
else
puts("YES");
}
return 0;
}
3. 差分约束
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int MAXN = 5e3 + 5;
int cnt;
int head[MAXN];
struct edge
{
int to, dis, nxt;
}e[MAXN << 1];
void add(int u, int v, int w)
{
e[++cnt] = edge{v, w, head[u]};
head[u] = cnt;
}
int dis[MAXN], inq[MAXN];
bool vis[MAXN];
queue<int> q;
int n, m;
bool spfa()
{
memset(dis, 0x3f, sizeof(dis));
dis[0] = 0;
vis[0] = true;
q.push(0);
while (!q.empty())
{
int u = q.front();
q.pop();
vis[u] = false;
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to, w = e[i].dis;
if (dis[v] > dis[u] + w)
{
dis[v] = dis[u] + w;
if (!vis[v])
{
if (++inq[v] > n)
{
return true;
}
vis[v] = true;
q.push(v);
}
}
}
}
return false;
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
{
add(0, i, 0);
}
for (int i = 1; i <= m; i++)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
add(v, u, w);
}
if (spfa())
{
puts("NO");
}
else
{
for (int i = 1; i <= n; i++)
{
printf("%d ", dis[i]);
}
}
return 0;
}
4. 最小生成树
(1) kruskal
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 5005;
const int MAXM = 2e5 + 5;
struct edge
{
int from, to, dis;
}e[MAXM];
bool cmp(edge x, edge y)
{
return x.dis < y.dis;
}
int n, m;
int fa[MAXN], dep[MAXN];
void init()
{
for (int i = 1; i <= n; i++)
{
fa[i] = i;
dep[i] = 1;
}
}
int find(int x)
{
if (x == fa[x])
{
return x;
}
return fa[x] = find(fa[x]);
}
int tot, ans;
void merge(int x, int y, int w)
{
x = find(x), y = find(y);
if (x != y)
{
tot++;
ans += w;
if (dep[x] > dep[y])
{
fa[y] = x;
}
else
{
fa[x] = y;
if (dep[x] == dep[y])
{
dep[y]++;
}
}
}
}
void kruskal()
{
sort(e + 1, e + m + 1, cmp);
for (int i = 1; i <= m; i++)
{
merge(e[i].from, e[i].to, e[i].dis);
if (tot == n - 1)
{
break;
}
}
}
int main()
{
scanf("%d%d", &n, &m);
init();
for (int i = 1; i <= m; i++)
{
scanf("%d%d%d", &e[i].from, &e[i].to, &e[i].dis);
}
kruskal();
if (tot < n - 1)
{
puts("orz");
return 0;
}
printf("%d\n", ans);
return 0;
}
(2) prim
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int MAXN = 5005;
const int MAXM = 2e5 + 5;
int cnt;
int head[MAXN];
struct edge
{
int to, dis, nxt;
}e[MAXM << 1];
void add(int u, int v, int w)
{
e[++cnt] = edge{v, w, head[u]};
head[u] = cnt;
}
bool vis[MAXN];
struct que
{
int pos, dis;
bool operator <(const que &x)const
{
return x.dis < dis;
}
};
priority_queue<que> pq;
int n, m, tot, ans;
void prim()
{
pq.push(que{1, 0});
while (!pq.empty())
{
int u = pq.top().pos, w = pq.top().dis;
pq.pop();
if (vis[u])
{
continue;
}
vis[u] = true;
ans += w;
if (++tot == n)
{
return;
}
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to, w = e[i].dis;
if (!vis[v])
{
pq.push(que{v, w});
}
}
}
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
add(u, v, w);
add(v, u, w);
}
prim();
if (tot < n)
{
puts("orz");
return 0;
}
printf("%d\n", ans);
return 0;
}
5. 强连通分量
(1) 模板
#include <iostream>
#include <cstdio>
#include <stack>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN = 1e4 + 5;
const int MAXM = 1e5 + 5;
int cnt;
int head[MAXN];
struct edge
{
int to, nxt;
}e[MAXM];
void add(int u, int v)
{
e[++cnt] = edge{v, head[u]};
head[u] = cnt;
}
int Time, tot;
int dfn[MAXN], low[MAXN], id[MAXN];
bool ins[MAXN];
stack<int> st;
vector<int> scc[MAXN];
void tarjan(int u)
{
dfn[u] = low[u] = ++Time;
st.push(u);
ins[u] = true;
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (!dfn[v])
{
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if (ins[v])
{
low[u] = min(low[u], dfn[v]);
}
}
if (dfn[u] == low[u])
{
tot++;
int v = 0;
while (v != u)
{
v = st.top();
st.pop();
ins[v] = false;
id[v] = tot;
scc[tot].push_back(v);
}
}
}
bool vis[MAXN];
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++)
{
int u, v;
scanf("%d%d", &u, &v);
add(u, v);
}
for (int i = 1; i <= n; i++)
{
if (!dfn[i])
{
tarjan(i);
}
}
printf("%d\n", tot);
for (int i = 1; i <= n; i++)
{
int x = id[i];
if (vis[x])
{
continue;
}
vis[x] = true;
sort(scc[x].begin(), scc[x].end());
for (int j = 0; j < scc[x].size(); j++)
{
printf("%d ", scc[x][j]);
}
puts("");
}
return 0;
}
(2) 缩点
#include <iostream>
#include <cstdio>
#include <stack>
#include <cstring>
#include <queue>
using namespace std;
const int MAXN = 1e4 + 5;
const int MAXM = 1e5 + 5;
int cnt;
int head[MAXN];
struct edge
{
int to, nxt;
}e[MAXM];
void add(int u, int v)
{
e[++cnt] = edge{v, head[u]};
head[u] = cnt;
}
int cnt_scc;
int head_scc[MAXN];
struct edge_scc
{
int to, nxt;
}e_scc[MAXM];
void add_scc(int u, int v)
{
e_scc[++cnt_scc] = edge_scc{v, head_scc[u]};
head_scc[u] = cnt_scc;
}
int Time, tot;
int dfn[MAXN], low[MAXN], sum[MAXN];
bool ins[MAXN];
stack<int> st;
int cntn, id[MAXN];
int a[MAXN];
void tarjan(int u)
{
dfn[u] = low[u] = ++Time;
st.push(u);
ins[u] = true;
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (!dfn[v])
{
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if (ins[v])
{
low[u] = min(low[u], dfn[v]);
}
}
if (dfn[u] == low[u])
{
int v = 0;
tot++;
while (v != u)
{
v = st.top();
st.pop();
sum[tot] += a[v];
id[v] = tot;
ins[v] = false;
}
}
}
int n, m;
int in[MAXN], dp[MAXN];
queue<int> q;
void topo()
{
for (int i = 1; i <= tot; i++)
{
if (!in[i])
{
q.push(i);
dp[i] = sum[i];
}
}
while (!q.empty())
{
int u = q.front();
q.pop();
for (int i = head_scc[u]; i; i = e_scc[i].nxt)
{
int v = e_scc[i].to;
dp[v] = max(dp[v], dp[u] + sum[v]);
if (!--in[v])
{
q.push(v);
}
}
}
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
{
scanf("%d", a + i);
}
for (int i = 1; i <= m; i++)
{
int u, v;
scanf("%d%d", &u, &v);
add(u, v);
}
for (int i = 1; i <= n; i++)
{
if (!dfn[i])
{
tarjan(i);
}
}
for (int u = 1; u <= n; u++)
{
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (id[u] != id[v])
{
add_scc(id[u], id[v]);
in[id[v]]++;
}
}
}
topo();
int ans = 0;
for (int i = 1; i <= tot; i++)
{
ans = max(ans, dp[i]);
}
printf("%d\n", ans);
return 0;
}
(3) 2-SAT
tips:数组全部要开 \(2\) 倍。
#include <iostream>
#include <cstdio>
#include <stack>
using namespace std;
const int MAXN = 1e6 + 5;
int cnt;
int head[MAXN << 1];
struct edge
{
int to, nxt;
}e[MAXN << 1];
void add(int u, int v)
{
e[++cnt] = edge{v, head[u]};
head[u] = cnt;
}
int Time, tot;
int dfn[MAXN << 1], low[MAXN << 1], id[MAXN << 1];
bool ins[MAXN << 1];
stack<int> st;
void tarjan(int u)
{
dfn[u] = low[u] = ++Time;
st.push(u);
ins[u] = true;
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (!dfn[v])
{
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if (ins[v])
{
low[u] = min(low[u], dfn[v]);
}
}
if (dfn[u] == low[u])
{
tot++;
int v = 0;
while (v != u)
{
v = st.top();
st.pop();
ins[v] = false;
id[v] = tot;
}
}
}
int main()
{
int n, m;
scanf("%d%d", &n, &m);
while (m--)
{
int i, a, j, b;
scanf("%d%d%d%d", &i, &a, &j, &b);
add(i + a * n, j + (1 - b) * n);
add(j + b * n, i + (1 - a) * n);
}
for (int i = 1; i <= (n << 1); i++)
{
if (!dfn[i])
{
tarjan(i);
}
}
for (int i = 1; i <= n; i++)
{
if (id[i] == id[i + n])
{
puts("IMPOSSIBLE");
return 0;
}
}
puts("POSSIBLE");
for (int i = 1; i <= n; i++)
{
printf("%d ", id[i] < id[i + n]);
}
return 0;
}
6. 割点与桥
(1) 割点
#include <iostream>
#include <cstdio>
using namespace std;
const int MAXN = 2e4 + 5;
const int MAXM = 1e5 + 5;
int cnt;
int head[MAXN];
struct edge
{
int to, nxt;
}e[MAXM << 1];
void add(int u, int v)
{
e[++cnt] = edge{v, head[u]};
head[u] = cnt;
}
int Time, rt, tot;
int dfn[MAXN], low[MAXN];
bool cut[MAXN];
void tarjan(int u)
{
dfn[u] = low[u] = ++Time;
int flag = 0;
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (!dfn[v])
{
tarjan(v);
low[u] = min(low[u], low[v]);
if (dfn[u] <= low[v])
{
if (u != rt || ++flag > 1)
{
if (!cut[u])
{
cut[u] = true;
tot++;
}
}
}
}
else
{
low[u] = min(low[u], dfn[v]);
}
}
}
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++)
{
int u, v;
scanf("%d%d", &u, &v);
add(u, v);
add(v, u);
}
for (int i = 1; i <= n; i++)
{
if (!dfn[i])
{
tarjan(rt = i);
}
}
printf("%d\n", tot);
for (int i = 1; i <= n; i++)
{
if (cut[i])
{
printf("%d ", i);
}
}
return 0;
}
(2) 桥
tips:
1. \(cnt\) 要初始化为 \(1\);
2. 要写成 i != (in_edge ^ 1)
而不是 i != in_edge ^ 1
。
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 155;
const int MAXM = 5005;
int cnt = 1;
int head[MAXN];
struct edge
{
int to, nxt;
}e[MAXM << 1];
void add(int u, int v)
{
e[++cnt] = edge{v, head[u]};
head[u] = cnt;
}
int tot;
struct bridge
{
int u, v;
}ans[MAXM];
void add_bridge(int u, int v)
{
ans[++tot] = bridge{min(u, v), max(u, v)};
}
bool cmp(bridge x, bridge y)
{
if (x.u == y.u)
{
return x.v < y.v;
}
return x.u < y.u;
}
int Time;
int dfn[MAXN], low[MAXN];
void tarjan(int u, int in_edge)
{
dfn[u] = low[u] = ++Time;
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (!dfn[v])
{
tarjan(v, i);
low[u] = min(low[u], low[v]);
if (dfn[u] < low[v])
{
add_bridge(u, v);
}
}
else if (i != (in_edge ^ 1))
{
low[u] = min(low[u], dfn[v]);
}
}
}
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++)
{
int u, v;
scanf("%d%d", &u, &v);
add(u, v);
add(v, u);
}
for (int i = 1; i <= n; i++)
{
if (!dfn[i])
{
tarjan(i, 0);
}
}
sort(ans + 1, ans + tot + 1, cmp);
for (int i = 1; i <= tot; i++)
{
printf("%d %d\n", ans[i].u, ans[i].v);
}
return 0;
}
7. 双连通分量
(1) 点双
#include <iostream>
#include <cstdio>
#include <stack>
#include <vector>
using namespace std;
const int MAXN = 5e4 + 5;
const int MAXM = 3e5 + 5;
int cnt;
int head[MAXN];
struct edge
{
int to, nxt;
}e[MAXM << 1];
void add(int u, int v)
{
e[++cnt] = edge{v, head[u]};
head[u] = cnt;
}
int Time, rt, tot;
int dfn[MAXN], low[MAXN];
stack<int> st;
vector<int> dcc[MAXN];
void tarjan(int u)
{
dfn[u] = low[u] = ++Time;
if (u == rt && !head[u])
{
dcc[++tot].push_back(u);
return;
}
st.push(u);
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (!dfn[v])
{
tarjan(v);
low[u] = min(low[u], low[v]);
if (dfn[u] <= low[v])
{
tot++;
int x = 0;
while (x != v)
{
x = st.top();
st.pop();
dcc[tot].push_back(x);
}
dcc[tot].push_back(u);
}
}
else
{
low[u] = min(low[u], dfn[v]);
}
}
}
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++)
{
int u, v;
scanf("%d%d", &u, &v);
add(u, v);
add(v, u);
}
for (int i = 1; i <= n; i++)
{
if (!dfn[i])
{
tarjan(rt = i);
}
}
for (int i = 1; i <= tot; i++)
{
for (int j = 0; j < dcc[i].size(); j++)
{
printf("%d ", dcc[i][j]);
}
puts("");
}
return 0;
}
(2) 边双
#include <iostream>
#include <cstdio>
using namespace std;
const int MAXN = 5e4 + 5;
const int MAXM = 3e5 + 5;
int cnt = 1;
int head[MAXN];
struct edge
{
int to, nxt;
}e[MAXM << 1];
void add(int u, int v)
{
e[++cnt] = edge{v, head[u]};
head[u] = cnt;
}
int Time;
int dfn[MAXN], low[MAXN];
bool bridge[MAXM << 1];
void tarjan(int u, int in_edge)
{
dfn[u] = low[u] = ++Time;
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (!dfn[v])
{
tarjan(v, i);
low[u] = min(low[u], low[v]);
if (dfn[u] < low[v])
{
bridge[i] = bridge[i ^ 1] = true;
}
}
else if (i != (in_edge ^ 1))
{
low[u] = min(low[u], dfn[v]);
}
}
}
bool vis[MAXN];
void dfs(int u)
{
vis[u] = true;
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (vis[v] || bridge[i])
{
continue;
}
dfs(v);
}
}
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++)
{
int u, v;
scanf("%d%d", &u, &v);
add(u, v);
add(v, u);
}
for (int i = 1; i <= n; i++)
{
if (!dfn[i])
{
tarjan(i, 0);
}
}
int tot = 0;
for (int i = 1; i <= n; i++)
{
if (!vis[i])
{
tot++;
dfs(i);
}
}
printf("%d\n", tot);
return 0;
}
8. 欧拉图
#include <iostream>
#include <cstdio>
#include <vector>
#include <stack>
#include <algorithm>
using namespace std;
const int MAXN = 1e5 + 5;
const int MAXM = 2e5 + 5;
vector<int> G[MAXN];
int in[MAXN], out[MAXN], st[MAXN];
stack<int> ans;
void dfs(int u)
{
for (int i = st[u]; i < G[u].size(); i = st[u])
{
st[u] = i + 1;
dfs(G[u][i]);
}
ans.push(u);
}
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++)
{
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
out[u]++, in[v]++;
}
int s = 0, cnt = 0;
for (int i = 1; i <= n; i++)
{
sort(G[i].begin(), G[i].end());
if (in[i] != out[i])
{
cnt++;
if (in[i] + 1 == out[i])
{
s = i;
}
}
}
if (cnt == 0)
{
s = 1;
}
else if (cnt != 2)
{
puts("No");
return 0;
}
dfs(s);
while (!ans.empty())
{
printf("%d ", ans.top());
ans.pop();
}
return 0;
}
9. LCA
#include <iostream>
#include <cstdio>
using namespace std;
const int MAXN = 5e5 + 5;
int cnt;
int head[MAXN];
struct edge
{
int to, nxt;
}e[MAXN << 1];
void add(int u, int v)
{
e[++cnt] = edge{v, head[u]};
head[u] = cnt;
}
int fa[MAXN][20], dep[MAXN];
void dfs(int u, int father)
{
fa[u][0] = father;
dep[u] = dep[father] + 1;
for (int i = 1; i <= 19; 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 == father)
{
continue;
}
dfs(v, u);
}
}
int lca(int x, int y)
{
if (dep[x] < dep[y])
{
swap(x, y);
}
for (int i = 19; i >= 0; i--)
{
if (dep[fa[x][i]] >= dep[y])
{
x = fa[x][i];
}
}
if (x == y)
{
return x;
}
for (int i = 19; i >= 0; i--)
{
if (fa[x][i] != fa[y][i])
{
x = fa[x][i], y = fa[y][i];
}
}
return fa[x][0];
}
int main()
{
int n, m, s;
scanf("%d%d%d", &n, &m, &s);
for (int i = 1; i < n; i++)
{
int u, v;
scanf("%d%d", &u, &v);
add(u, v);
add(v, u);
}
dfs(s, 0);
while (m--)
{
int a, b;
scanf("%d%d", &a, &b);
printf("%d\n", lca(a, b));
}
return 0;
}
三、数据结构
1. 堆
#include <iostream>
#include <cstdio>
using namespace std;
const int MAXN = 1e6 + 5;
int tot;
int hp[MAXN];
void push(int x)
{
hp[++tot] = x;
int pos = tot;
while (tot)
{
int fa = pos >> 1;
if (hp[pos] < hp[fa])
{
swap(hp[pos], hp[fa]);
}
else
{
break;
}
pos = fa;
}
}
void pop()
{
swap(hp[1], hp[tot--]);
int pos = 1;
while ((pos << 1) <= tot)
{
int lson = pos << 1, rson = pos << 1 | 1;
if (rson <= tot && hp[rson] < hp[lson])
{
lson++;
}
if (hp[lson] < hp[pos])
{
swap(hp[lson], hp[pos]);
}
else
{
break;
}
pos = lson;
}
}
int main()
{
int n;
scanf("%d", &n);
while (n--)
{
int op, x;
scanf("%d", &op);
if (op == 1)
{
scanf("%d", &x);
push(x);
}
else if (op == 2)
{
printf("%d\n", hp[1]);
}
else
{
pop();
}
}
return 0;
}
2. 并查集(路径压缩+按秩合并)
#include <iostream>
#include <cstdio>
using namespace std;
const int MAXN = 1e4 + 5;
int n, m;
int fa[MAXN], dep[MAXN];
void init()
{
for (int i = 1; i <= n; i++)
{
fa[i] = i;
}
}
int find(int x)
{
if (x == fa[x])
{
return x;
}
return fa[x] = find(fa[x]);
}
void merge(int x, int y)
{
x = find(x), y = find(y);
if (x != y)
{
if (dep[x] > dep[y])
{
fa[y] = x;
}
else
{
fa[x] = y;
if (dep[x] == dep[y])
{
dep[y]++;
}
}
}
}
int main()
{
scanf("%d%d", &n, &m);
init();
while (m--)
{
int z, x, y;
scanf("%d%d%d", &z, &x, &y);
if (z == 1)
{
merge(x, y);
}
else
{
if (find(x) == find(y))
{
puts("Y");
}
else
{
puts("N");
}
}
}
return 0;
}
3. 线段树
#include <iostream>
#include <cstdio>
#define int long long
using namespace std;
const int MAXN = 1e5 + 5;
int a[MAXN];
#define lson pos << 1
#define rson pos << 1 | 1
struct tree
{
int l, r, siz, val, tag = 0;
}t[MAXN << 2];
void pushup(int pos)
{
t[pos].val = t[lson].val + t[rson].val;
}
void pushdown(int pos)
{
if (t[pos].tag)
{
t[lson].val += t[pos].tag * t[lson].siz;
t[rson].val += t[pos].tag * t[rson].siz;
t[lson].tag += t[pos].tag;
t[rson].tag += t[pos].tag;
t[pos].tag = 0;
}
}
void build(int pos, int l, int r)
{
t[pos].l = l, t[pos].r = r, t[pos].siz = r - l + 1;
if (l == r)
{
t[pos].val = a[l];
return;
}
int mid = (l + r) >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
pushup(pos);
}
void update(int pos, int L, int R, int k)
{
int l = t[pos].l, r = t[pos].r;
if (L <= l && r <= R)
{
t[pos].val += k * t[pos].siz;
t[pos].tag += k;
return;
}
pushdown(pos);
int mid = (l + r) >> 1;
if (L <= mid)
{
update(lson, L, R, k);
}
if (R > mid)
{
update(rson, L, R, k);
}
pushup(pos);
}
int query(int pos, int L, int R)
{
int l = t[pos].l, r = t[pos].r;
if (L <= l && r <= R)
{
return t[pos].val;
}
pushdown(pos);
int mid = (l + r) >> 1, res = 0;
if (L <= mid)
{
res = query(lson, L, R);
}
if (R > mid)
{
res += query(rson, L, R);
}
return res;
}
signed main()
{
int n, m;
scanf("%lld%lld", &n, &m);
for (int i = 1; i <= n; i++)
{
scanf("%lld", a + i);
}
build(1, 1, n);
while (m--)
{
int op, x, y, k;
scanf("%lld%lld%lld", &op, &x, &y);
if (op == 1)
{
scanf("%lld", &k);
update(1, x, y, k);
}
else
{
printf("%lld\n", query(1, x, y));
}
}
return 0;
}
4. 树状数组
(1) 一维单修区查
#include <iostream>
#include <cstdio>
using namespace std;
const int MAXN = 5e5 + 5;
int n, m;
int c[MAXN];
int lowbit(int x)
{
return x & -x;
}
void update(int x, int k)
{
for (int i = x; i <= n; i += lowbit(i))
{
c[i] += k;
}
}
int query(int x)
{
int res = 0;
for (int i = x; i; i -= lowbit(i))
{
res += c[i];
}
return res;
}
int query_range(int l, int r)
{
return query(r) - query(l - 1);
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
{
int x;
scanf("%d", &x);
update(i, x);
}
while (m--)
{
int op, x, y;
scanf("%d%d%d", &op, &x, &y);
if (op == 1)
{
update(x, y);
}
else
{
printf("%d\n", query_range(x, y));
}
}
return 0;
}
(2) 一维区修单查
#include <iostream>
#include <cstdio>
using namespace std;
const int MAXN = 5e5 + 5;
int n, m;
int c[MAXN];
int lowbit(int x)
{
return x & -x;
}
void update(int x, int k)
{
for (int i = x; i <= n; i += lowbit(i))
{
c[i] += k;
}
}
void update_range(int l, int r, int k)
{
update(l, k);
update(r + 1, -k);
}
int query(int x)
{
int res = 0;
for (int i = x; i; i -= lowbit(i))
{
res += c[i];
}
return res;
}
int main()
{
scanf("%d%d", &n, &m);
int now, lst = 0;
for (int i = 1; i <= n; i++)
{
scanf("%d", &now);
update(i, now - lst);
lst = now;
}
while (m--)
{
int op, x, y, k;
scanf("%d%d", &op, &x);
if (op == 1)
{
scanf("%d%d", &y, &k);
update_range(x, y, k);
}
else
{
printf("%d\n", query(x));
}
}
return 0;
}
(3) 二维区修区查
#include <iostream>
#include <cstdio>
using namespace std;
const int MAXN = 2050;
int n, m;
int c1[MAXN][MAXN], c2[MAXN][MAXN], c3[MAXN][MAXN], c4[MAXN][MAXN];
int lowbit(int x)
{
return x & -x;
}
void update(int x, int y, int k)
{
for (int i = x; i <= n; i += lowbit(i))
{
for (int j = y; j <= m; j += lowbit(j))
{
c1[i][j] += k;
c2[i][j] += k * x;
c3[i][j] += k * y;
c4[i][j] += k * x * y;
}
}
}
void update_range(int a, int b, int c, int d, int k)
{
update(a, b, k);
update(a, d + 1, -k);
update(c + 1, b, -k);
update(c + 1, d + 1, k);
}
int query(int x, int y)
{
int res = 0;
for (int i = x; i; i -= lowbit(i))
{
for (int j = y; j; j -= lowbit(j))
{
res += c1[i][j] * (x + 1) * (y + 1) - c2[i][j] * (y + 1) - c3[i][j] * (x + 1) + c4[i][j];
}
}
return res;
}
int query_range(int a, int b, int c, int d)
{
return query(c, d) - query(a - 1, d) - query(c, b - 1) + query(a - 1, b - 1);
}
int main()
{
char op[3];
int a, b, c, d, k;
scanf("%s%d%d", op, &n, &m);
while (~scanf("%s%d%d%d%d", op, &a, &b, &c, &d))
{
if (op[0] == 'L')
{
scanf("%d", &k);
update_range(a, b, c, d, k);
}
else
{
printf("%d\n", query_range(a, b, c, d));
}
}
return 0;
}
5. 轻重链剖分
#include <iostream>
#include <cstdio>
using namespace std;
const int MAXN = 1e5 + 5;
int cnt;
int head[MAXN];
struct edge
{
int to, nxt;
}e[MAXN << 1];
void add(int u, int v)
{
e[++cnt] = edge{v, head[u]};
head[u] = cnt;
}
int Time;
int fa[MAXN], dep[MAXN], siz[MAXN], son[MAXN], dfn[MAXN], top[MAXN];
void dfs1(int u, int father)
{
fa[u] = father;
dep[u] = dep[father] + 1;
siz[u] = 1;
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (v == father)
{
continue;
}
dfs1(v, u);
siz[u] += siz[v];
if (siz[v] > siz[son[u]])
{
son[u] = v;
}
}
}
void dfs2(int u, int topp)
{
dfn[u] = ++Time;
top[u] = topp;
if (son[u])
{
dfs2(son[u], topp);
}
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (top[v])
{
continue;
}
dfs2(v, v);
}
}
int n, m, r, p;
int a[MAXN], b[MAXN];
#define lson pos << 1
#define rson pos << 1 | 1
struct tree
{
int l, r, siz, val, tag = 0;
}t[MAXN << 2];
void pushup(int pos)
{
t[pos].val = (t[lson].val + t[rson].val) % p;
}
void pushdown(int pos)
{
if (t[pos].tag)
{
t[lson].val = (t[lson].val + t[pos].tag * t[lson].siz) % p;
t[rson].val = (t[rson].val + t[pos].tag * t[rson].siz) % p;
t[lson].tag = (t[lson].tag + t[pos].tag) % p;
t[rson].tag = (t[rson].tag + t[pos].tag) % p;
t[pos].tag = 0;
}
}
void build(int pos, int l, int r)
{
t[pos].l = l, t[pos].r = r, t[pos].siz = r - l + 1;
if (l == r)
{
t[pos].val = b[l] % p;
return;
}
int mid = (l + r) >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
pushup(pos);
}
void update(int pos, int L, int R, int k)
{
int l = t[pos].l, r = t[pos].r;
if (L <= l && r <= R)
{
t[pos].val = (t[pos].val + k * t[pos].siz) % p;
t[pos].tag = (t[pos].tag + k) % p;
return;
}
pushdown(pos);
int mid = (l + r) >> 1;
if (L <= mid)
{
update(lson, L, R, k);
}
if (R > mid)
{
update(rson, L, R, k);
}
pushup(pos);
}
int query(int pos, int L, int R)
{
int l = t[pos].l, r = t[pos].r;
if (L <= l && r <= R)
{
return t[pos].val;
}
pushdown(pos);
int mid = (l + r) >> 1, res = 0;
if (L <= mid)
{
res = query(lson, L, R);
}
if (R > mid)
{
res = (res + query(rson, L, R)) % p;
}
return res;
}
void update_path(int x, int y, int z)
{
z %= p;
while (top[x] != top[y])
{
if (dep[top[x]] < dep[top[y]])
{
swap(x, y);
}
update(1, dfn[top[x]], dfn[x], z);
x = fa[top[x]];
}
if (dep[x] > dep[y])
{
swap(x, y);
}
update(1, dfn[x], dfn[y], z);
}
void update_subtree(int x, int z)
{
z %= p;
update(1, dfn[x], dfn[x] + siz[x] - 1, z);
}
int query_path(int x, int y)
{
int res = 0;
while (top[x] != top[y])
{
if (dep[top[x]] < dep[top[y]])
{
swap(x, y);
}
res = (res + query(1, dfn[top[x]], dfn[x])) % p;
x = fa[top[x]];
}
if (dep[x] > dep[y])
{
swap(x, y);
}
res = (res + query(1, dfn[x], dfn[y])) % p;
return res;
}
int query_subtree(int x)
{
return query(1, dfn[x], dfn[x] + siz[x] - 1);
}
int main()
{
scanf("%d%d%d%d", &n, &m, &r, &p);
for (int i = 1; i <= n; i++)
{
scanf("%d", a + i);
}
for (int i = 1; i < n; i++)
{
int x, y;
scanf("%d%d", &x, &y);
add(x, y);
add(y, x);
}
dfs1(r, 0);
dfs2(r, r);
for (int i = 1; i <= n; i++)
{
b[dfn[i]] = a[i];
}
build(1, 1, n);
while (m--)
{
int op, x, y, z;
scanf("%d%d", &op, &x);
if (op == 1)
{
scanf("%d%d", &y, &z);
update_path(x, y, z);
}
else if (op == 2)
{
scanf("%d", &y);
printf("%d\n", query_path(x, y));
}
else if (op == 3)
{
scanf("%d", &z);
update_subtree(x, z);
}
else
{
printf("%d\n", query_subtree(x));
}
}
return 0;
}
6. ST 表
tips:注意 lg
数组从 i = 2
开始预处理。
#include <iostream>
#include <cstdio>
using namespace std;
const int MAXN = 1e5 + 5;
int n, m;
int lg[MAXN], st[MAXN][18];
void init()
{
for (int j = 1; (1 << j) <= 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]);
}
}
}
int query(int l, int r)
{
int k = lg[r - l + 1];
return max(st[l][k], st[r - (1 << k) + 1][k]);
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 2; i <= n; i++)
{
lg[i] = lg[i >> 1] + 1;
}
for (int i = 1; i <= n; i++)
{
scanf("%d", &st[i][0]);
}
init();
while (m--)
{
int l, r;
scanf("%d%d", &l, &r);
printf("%d\n", query(l, r));
}
return 0;
}
7. 单调类
(1) 单调队列
#include <iostream>
#include <cstdio>
using namespace std;
const int MAXN = 1e6 + 5;
int a[MAXN], q[MAXN];
int main()
{
int n, k;
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++)
{
scanf("%d", a + i);
}
int head = 1, tail = 0;
for (int i = 1; i <= n; i++)
{
while (head <= tail && i - q[head] >= k)
{
head++;
}
while (head <= tail && a[q[tail]] >= a[i])
{
tail--;
}
q[++tail] = i;
if (i >= k)
{
printf("%d ", a[q[head]]);
}
}
puts("");
head = 1, tail = 0;
for (int i = 1; i <= n; i++)
{
while (head <= tail && i - q[head] >= k)
{
head++;
}
while (head <= tail && a[q[tail]] <= a[i])
{
tail--;
}
q[++tail] = i;
if (i >= k)
{
printf("%d ", a[q[head]]);
}
}
return 0;
}
(2) 单调栈
#include <iostream>
#include <cstdio>
using namespace std;
const int MAXN = 3e6 + 5;
int a[MAXN], st[MAXN], ans[MAXN];
int main()
{
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%d", a + i);
}
int top = 0;
for (int i = n; i >= 1; i--)
{
while (top && a[st[top]] <= a[i])
{
top--;
}
ans[i] = st[top];
st[++top] = i;
}
for (int i = 1; i <= n; i++)
{
printf("%d ", ans[i]);
}
return 0;
}
8. 平衡树
#include <iostream>
#include <cstdio>
#include <cstdlib>
using namespace std;
const int MAXN = 1e6 + 5;
int tot, rt;
struct treap
{
int lson, rson, val, key, siz;
}t[MAXN];
int build(int val)
{
t[++tot] = treap{0, 0, val, rand(), 1};
return tot;
}
void pushup(int pos)
{
t[pos].siz = t[t[pos].lson].siz + t[t[pos].rson].siz + 1;
}
void split(int pos, int val, int &a, int &b)
{
if (!pos)
{
a = b = 0;
return;
}
if (t[pos].val <= val)
{
a = pos;
split(t[pos].rson, val, t[a].rson, b);
}
else
{
b = pos;
split(t[pos].lson, val, a, t[b].lson);
}
pushup(pos);
}
int merge(int a, int b)
{
if (!a || !b)
{
return a + b;
}
if (t[a].key > t[b].key)
{
t[a].rson = merge(t[a].rson, b);
pushup(a);
return a;
}
else
{
t[b].lson = merge(a, t[b].lson);
pushup(b);
return b;
}
}
void insert(int x)
{
int a = 0, b = 0, c = build(x);
split(rt, x, a, b);
rt = merge(merge(a, c), b);
}
void remove(int x)
{
int a = 0, b = 0, c = 0;
split(rt, x - 1, a, b);
split(b, x, b, c);
b = merge(t[b].lson, t[b].rson);
rt = merge(a, merge(b, c));
}
int getrnk(int x)
{
int a = 0, b = 0;
split(rt, x - 1, a, b);
int res = t[a].siz + 1;
rt = merge(a, b);
return res;
}
int getval(int x)
{
int pos = rt;
while (pos)
{
if (t[t[pos].lson].siz + 1 == x)
{
break;
}
if (t[t[pos].lson].siz >= x)
{
pos = t[pos].lson;
}
else
{
x -= (t[t[pos].lson].siz + 1);
pos = t[pos].rson;
}
}
return t[pos].val;
}
int getpre(int x)
{
int a = 0, b = 0;
split(rt, x - 1, a, b);
int pos = a;
while (t[pos].rson)
{
pos = t[pos].rson;
}
rt = merge(a, b);
return t[pos].val;
}
int getnxt(int x)
{
int a = 0, b = 0;
split(rt, x, a, b);
int pos = b;
while (t[pos].lson)
{
pos = t[pos].lson;
}
rt = merge(a, b);
return t[pos].val;
}
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
{
int x;
scanf("%d", &x);
insert(x);
}
int lst = 0, ans = 0;
while (m--)
{
int op, x;
scanf("%d%d", &op, &x);
x ^= lst;
if (op == 1)
{
insert(x);
}
else if (op == 2)
{
remove(x);
}
else if (op == 3)
{
ans ^= (lst = getrnk(x));
}
else if (op == 4)
{
ans ^= (lst = getval(x));
}
else if (op == 5)
{
ans ^= (lst = getpre(x));
}
else
{
ans ^= (lst = getnxt(x));
}
}
printf("%d\n", ans);
return 0;
}
9. 分块
#include <iostream>
#include <cstdio>
#include <cmath>
#define int long long
using namespace std;
const int MAXN = 1e5 + 5;
int a[MAXN], block[MAXN], L[MAXN], R[MAXN], sum[MAXN], add[MAXN];
void update(int l, int r, int k)
{
int p = block[l], q = block[r];
if (p == q)
{
for (int i = l; i <= r; i++)
{
a[i] += k;
}
sum[p] += k * (r - l + 1);
}
else
{
for (int i = l; i <= R[p]; i++)
{
a[i] += k;
}
sum[p] += k * (R[p] - l + 1);
for (int i = p + 1; i < q; i++)
{
add[i] += k;
}
for (int i = L[q]; i <= r; i++)
{
a[i] += k;
}
sum[q] += k * (r - L[q] + 1);
}
}
int query(int l, int r)
{
int p = block[l], q = block[r], res = 0;
if (p == q)
{
for (int i = l; i <= r; i++)
{
res += a[i];
}
res += add[p] * (r - l + 1);
}
else
{
for (int i = l; i <= R[p]; i++)
{
res += a[i];
}
res += add[p] * (R[p] - l + 1);
for (int i = p + 1; i < q; i++)
{
res += sum[i] + add[i] * (R[i] - L[i] + 1);
}
for (int i = L[q]; i <= r; i++)
{
res += a[i];
}
res += add[q] * (r - L[q] + 1);
}
return res;
}
signed main()
{
int n, m;
scanf("%lld%lld", &n, &m);
for (int i = 1; i <= n; i++)
{
scanf("%lld", a + i);
}
int k = sqrt(n);
for (int i = 1; i <= k; i++)
{
L[i] = R[i - 1] + 1;
R[i] = i * k;
}
if (R[k] < n)
{
k++;
L[k] = R[k - 1] + 1;
R[k] = n;
}
for (int i = 1; i <= k; i++)
{
for (int j = L[i]; j <= R[i]; j++)
{
block[j] = i;
sum[i] += a[j];
}
}
while (m--)
{
int op, x, y, k;
scanf("%lld%lld%lld", &op, &x, &y);
if (op == 1)
{
scanf("%lld", &k);
update(x, y, k);
}
else
{
printf("%lld\n", query(x, y));
}
}
return 0;
}
四、数学
1. 质数
#include <iostream>
#include <cstdio>
using namespace std;
const int MAXN = 1e8 + 5;
int p[MAXN];
bool vis[MAXN];
void pre(int n)
{
for (int i = 2; i <= n; i++)
{
if (!vis[i])
{
p[++p[0]] = i;
}
for (int j = 1; j <= p[0] && i * p[j] <= n; j++)
{
if (i % p[j] == 0)
{
break;
}
vis[i * p[j]] = true;
}
}
}
int main()
{
int n, q;
scanf("%d%d", &n, &q);
pre(n);
while (q--)
{
int k;
scanf("%d", &k);
printf("%d\n", p[k]);
}
return 0;
}
2. 快速幂
tips:不能直接 printf("%lld^%lld mod %lld=%lld\n", a, b, p, ksm());
,因为是从后往前处理,输出 \(b\) 时快速幂已经结束,\(b=0\)。
#include <iostream>
#include <cstdio>
#define int long long
using namespace std;
int a, b, p;
int ksm()
{
int base = a, ans = 1ll;
while (b)
{
if (b & 1ll)
{
ans = ans * base % p;
}
base = base * base % p;
b >>= 1ll;
}
return ans;
}
signed main()
{
scanf("%lld%lld%lld", &a, &b, &p);
printf("%lld^%lld mod %lld=", a, b, p);
printf("%lld\n", ksm());
return 0;
}
3. 矩阵
(1) 矩阵快速幂
#include <iostream>
#include <cstdio>
#include <cstring>
#define int long long
using namespace std;
const int MAXN = 105;
const int MOD = 1e9 + 7;
int n, k;
struct matrix
{
int a[MAXN][MAXN];
void init()
{
memset(a, 0, sizeof(a));
}
void build()
{
for (int i = 1; i <= n; i++)
{
a[i][i] = 1;
}
}
matrix operator *(const matrix &x)const
{
matrix res;
res.init();
for (int k = 1; k <= n; k++)
{
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
res.a[i][j] = (res.a[i][j] + x.a[i][k] * a[k][j]) % MOD;
}
}
}
return res;
}
};
matrix ksm(matrix a, int b)
{
matrix base = a, ans;
ans.init(), ans.build();
while (b)
{
if (b & 1)
{
ans = ans * base;
}
base = base * base;
b >>= 1;
}
return ans;
}
signed main()
{
scanf("%lld%lld", &n, &k);
matrix a;
a.init();
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
scanf("%lld", &a.a[i][j]);
}
}
matrix ans = ksm(a, k);
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
printf("%lld ", ans.a[i][j]);
}
puts("");
}
return 0;
}
(2) 矩阵加速数列
#include <iostream>
#include <cstdio>
#include <cstring>
#define int long long
using namespace std;
const int MOD = 1e9 + 7;
struct matrix
{
int a[4][4];
void init()
{
memset(a, 0, sizeof(a));
}
void build()
{
for (int i = 1; i <= 3; i++)
{
a[1][i] = 1;
}
}
matrix operator *(const matrix &x)const
{
matrix res;
res.init();
for (int k = 1; k <= 3; k++)
{
for (int i = 1; i <= 3; i++)
{
for (int j = 1; j <= 3; j++)
{
res.a[i][j] = (res.a[i][j] + a[i][k] * x.a[k][j]) % MOD;
}
}
}
return res;
}
};
matrix ksm(int b)
{
matrix base, ans;
base.init();
base.a[1][1] = base.a[1][2] = base.a[2][3] = base.a[3][1] = 1;
ans.init(), ans.build();
while (b)
{
if (b & 1)
{
ans = ans * base;
}
base = base * base;
b >>= 1;
}
return ans;
}
signed main()
{
int t;
scanf("%lld", &t);
while (t--)
{
int n;
scanf("%lld", &n);
if (n <= 3)
{
puts("1");
}
else
{
printf("%lld\n", ksm(n - 3).a[1][1]);
}
}
return 0;
}
4. 博弈论
#include <iostream>
#include <cstdio>
using namespace std;
const int MAXN = 1e4 + 5;
int t, n;
int a[MAXN];
int xor_sum()
{
int ans = 0;
for (int i = 1; i <= n; i++)
{
ans ^= a[i];
}
return ans;
}
int main()
{
scanf("%d", &t);
while (t--)
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%d", a + i);
}
if (!xor_sum())
{
puts("No");
}
else
{
puts("Yes");
}
}
return 0;
}
5. exgcd
#include <iostream>
#include <cstdio>
#define int long long
using namespace std;
int x, y;
void exgcd(int a, int b)
{
if (!b)
{
x = 1, y = 0;
return;
}
exgcd(b, a % b);
int tmp = x;
x = y;
y = tmp - a / b * y;
}
signed main()
{
int a, b;
scanf("%lld%lld", &a, &b);
exgcd(a, b);
printf("%lld\n", (x % b + b) % b);
return 0;
}
6. 逆元
(1) 单个求
#include <iostream>
#include <cstdio>
#define int long long
#define re register
using namespace std;
const int MOD = 19260817;
inline int read()
{
re int f = 1, x = 0;
re char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-')
{
f = -1;
}
c = getchar();
}
while (c >= '0' && c <= '9')
{
x = ((x << 3) + (x << 1) + c - '0') % MOD;
c = getchar();
}
return f * x;
}
int ksm(int a, int b)
{
int base = a, ans = 1;
while (b)
{
if (b & 1)
{
ans = ans * base % MOD;
}
base = base * base % MOD;
b >>= 1;
}
return ans;
}
signed main()
{
int a = read(), b = read();
if (b)
{
printf("%lld\n", a * ksm(b, MOD - 2) % MOD);
}
else
{
puts("Angry!");
}
return 0;
}
(2) 线性推
#include <iostream>
#include <cstdio>
#define int long long
using namespace std;
const int MAXN = 3e6 + 5;
int inv[MAXN];
signed main()
{
int n, p;
scanf("%lld%lld", &n, &p);
inv[1] = 1;
puts("1");
for (int i = 2; i <= n; i++)
{
inv[i] = (-(p / i) * inv[p % i] % p + p) % p;
printf("%lld\n", inv[i]);
}
return 0;
}
7. 扩展欧拉定理
#include <iostream>
#include <cstdio>
#define int long long
using namespace std;
int a, m, b, phi;
void pre()
{
phi = m;
int tm = m;
for (int i = 2; i * i <= tm; i++)
{
if (tm % i == 0)
{
phi = phi / i * (i - 1);
while (tm % i == 0)
{
tm /= i;
}
}
}
if (tm > 1)
{
phi = phi / tm * (tm - 1);
}
}
bool flag;
inline int read()
{
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-')
{
f = -1;
}
c = getchar();
}
while (c >= '0' && c <= '9')
{
x = (x << 3) + (x << 1) + c - '0';
if (x >= phi)
{
flag = true;
}
x %= phi;
c = getchar();
}
return x * f;
}
int ksm(int a, int b)
{
int base = a, ans = 1;
while (b)
{
if (b & 1)
{
ans = ans * base % m;
}
base = base * base % m;
b >>= 1;
}
return ans;
}
signed main()
{
scanf("%lld%lld", &a, &m);
pre();
b = read();
if (flag)
{
b += phi;
}
printf("%lld\n", ksm(a, b));
return 0;
}
8. 裴蜀定理
#include <iostream>
#include <cstdio>
using namespace std;
int gcd(int a, int b)
{
if (!b)
{
return a;
}
return gcd(b, a % b);
}
int main()
{
int n, ans = 0;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
int a;
scanf("%d", &a);
a = abs(a);
ans = gcd(ans, a);
}
printf("%d\n", ans);
return 0;
}
五、字符串
1. KMP
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 1e6 + 5;
char s1[MAXN], s2[MAXN];
int nxt[MAXN];
int main()
{
scanf("%s%s", s1 + 1, s2 + 1);
int n = strlen(s1 + 1), m = strlen(s2 + 1);
for (int i = 2, j = 0; i <= m; i++)
{
while (j && s2[i] != s2[j + 1])
{
j = nxt[j];
}
if (s2[i] == s2[j + 1])
{
j++;
}
nxt[i] = j;
}
for (int i = 1, j = 0; i <= n; i++)
{
while (j && s1[i] != s2[j + 1])
{
j = nxt[j];
}
if (s1[i] == s2[j + 1])
{
j++;
}
if (j == m)
{
printf("%d\n", i - m + 1);
}
}
for (int i = 1; i <= m; i++)
{
printf("%d ", nxt[i]);
}
return 0;
}
2. manacher
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 1.1e7 + 5;
int n, l;
char a[MAXN], s[MAXN << 1];
void init()
{
n = strlen(a + 1);
s[++l] = '$';
s[++l] = '#';
for (int i = 1; i <= n; i++)
{
s[++l] = a[i];
s[++l] = '#';
}
}
int len[MAXN << 1];
void manacher()
{
int maxr = 0, mid = 0;
for (int i = 1; i <= l; i++)
{
if (i <= maxr)
{
len[i] = min(maxr - i + 1, len[(mid << 1) - i]);
}
while (s[i - len[i]] == s[i + len[i]])
{
len[i]++;
}
if (i + len[i] - 1 > maxr)
{
maxr = i + len[i] - 1;
mid = i;
}
}
}
int main()
{
scanf("%s", a + 1);
init();
manacher();
int ans = 0;
for (int i = 1; i <= l; i++)
{
ans = max(ans, len[i] - 1);
}
printf("%d\n", ans);
return 0;
}
3. trie 树
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 1e4 + 5;
int tot;
int trie[MAXN * 55][26];
void insert(char *s)
{
int n = strlen(s), pos = 0;
for (int i = 0; i < n; i++)
{
int c = s[i] - 'a';
if (!trie[pos][c])
{
trie[pos][c] = ++tot;
}
pos = trie[pos][c];
}
}
bool vis[MAXN * 55];
int search(char *s)
{
int n = strlen(s), pos = 0;
for (int i = 0; i < n; i++)
{
int c = s[i] - 'a';
if (!trie[pos][c])
{
return 0;
}
pos = trie[pos][c];
}
if (!vis[pos])
{
vis[pos] = true;
return 1;
}
else
{
return 2;
}
}
char s[55];
int main()
{
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%s", s);
insert(s);
}
int m;
scanf("%d", &m);
while (m--)
{
scanf("%s", s);
int res = search(s);
if (!res)
{
puts("WRONG");
}
else if (res == 1)
{
puts("OK");
}
else
{
puts("REPEAT");
}
}
return 0;
}
4. AC 自动机
(1) 模板
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int MAXN = 1e6 + 5;
int tot;
int trie[MAXN][26], ed[MAXN], fail[MAXN];
bool vis[MAXN];
void insert(char *s)
{
int n = strlen(s), pos = 0;
for (int i = 0; i < n; i++)
{
int c = s[i] - 'a';
if (!trie[pos][c])
{
trie[pos][c] = ++tot;
}
pos = trie[pos][c];
}
ed[pos]++;
}
int search(char *s)
{
int n = strlen(s), pos = 0, ans = 0;
for (int i = 0; i < n; i++)
{
int c = s[i] - 'a';
pos = trie[pos][c];
for (int j = pos; j && !vis[j]; j = fail[j])
{
ans += ed[j];
vis[j] = true;
}
}
return ans;
}
queue<int> q;
void build()
{
for (int c = 0; c < 26; c++)
{
if (trie[0][c])
{
q.push(trie[0][c]);
}
}
while (!q.empty())
{
int u = q.front();
q.pop();
for (int c = 0; c < 26; c++)
{
int v = trie[u][c];
if (v)
{
fail[v] = trie[fail[u]][c];
q.push(v);
}
else
{
trie[u][c] = trie[fail[u]][c];
}
}
}
}
char s[MAXN], t[MAXN];
int main()
{
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%s", s);
insert(s);
}
build();
scanf("%s", t);
printf("%d\n", search(t));
return 0;
}
(2) 拓扑优化
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int MAXN = 2e5 + 5;
int cnt;
int head[MAXN], siz[MAXN];
struct edge
{
int to, nxt;
}e[MAXN];
void add(int u, int v)
{
e[++cnt] = edge{v, head[u]};
head[u] = cnt;
}
void dfs(int u)
{
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
dfs(v);
siz[u] += siz[v];
}
}
int tot;
int trie[MAXN][26], id[MAXN], fail[MAXN];
void insert(char *s, int num)
{
int n = strlen(s), pos = 0;
for (int i = 0; i < n; i++)
{
int c = s[i] - 'a';
if (!trie[pos][c])
{
trie[pos][c] = ++tot;
}
pos = trie[pos][c];
}
id[num] = pos;
}
void search(char *s)
{
int n = strlen(s), pos = 0;
for (int i = 0; i < n; i++)
{
int c = s[i] - 'a';
pos = trie[pos][c];
siz[pos]++;
}
for (int i = 1; i <= tot; i++)
{
add(fail[i], i);
}
}
queue<int> q;
void build()
{
for (int c = 0; c < 26; c++)
{
if (trie[0][c])
{
q.push(trie[0][c]);
}
}
while (!q.empty())
{
int u = q.front();
q.pop();
for (int c = 0; c < 26; c++)
{
int v = trie[u][c];
if (v)
{
fail[v] = trie[fail[u]][c];
q.push(v);
}
else
{
trie[u][c] = trie[fail[u]][c];
}
}
}
}
char t[2000005], s[2000005];
int main()
{
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%s", t);
insert(t, i);
}
build();
scanf("%s", s);
search(s);
dfs(0);
for (int i = 1; i <= n; i++)
{
printf("%d\n", siz[id[i]]);
}
return 0;
}