CSP-S考前整理合集
复赛前整理整理……涨涨RP
本文主要收集整理各类板子的代码。
一·IO优化
快速读入
template<class T>
inline T read(T &x) {
x = 0; int w = 1, ch = getchar();
for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') w = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - 48;
return x *= w;
}
输出
输出不再写单独函数
答案输出用printf,调试信息一律std::cout<<……
二·数论
欧几里得算法
用处:求最大公约数
int gcd(int a, int b) {return !b ? a : gcd(b, a % b);}
int main() {
int a, b;
read(a), read(b);
int G = gcd(a, b);
}
时间复杂度:\(O(logn)\)
扩展欧几里得算法
用处:求解逆元,解线性同余方程……
void exgcd(int a, int b, int &d, int &x, int &y) {
if (!b) {d = a; x = 1; y = 0; return ;}
else exgcd(b, a % b, d, y, x), y -= a / b * x;
}
int main() {
int a, b, x, y;
exgcd(a, p, x, y);
int G = (x % p + p) % p; // a 在 %p意义下的逆元
}
时间复杂度:\(O(logn)\)
快速幂
用处:求解逆元……
#define LL long long
const int MOD = 1e9 + 7;
LL quick_pow(LL a, LL b, LL mod) {
LL ans = 1;
while (b) {
if (b & 1) ans = ans * a % mod;
b >>= 1;
a = a * a % mod;
}
return ans % mod;
}
int main() {
LL a, b;
LL G = quick_pow(a, b, MOD); // a ^ b % MOD
}
时间复杂度:\(O(logn)\)
龟速乘
用处:乘法爆\(long\ long\)时使用
#define LL long long
const int MOD = 1e9 + 7;
LL quick_mul(LL a, LL b, LL mod) {
LL ans = 1;
while (b) {
if (b & 1) ans = (ans + a) % mod;
b >>= 1;
a = (a + a) % mod;
}
return ans % mod;
}
int main() {
LL a, b;
LL G = quick_mul(a, b, MOD); // a * b % MOD
}
时间复杂度:\(O(logn)\)
逆元
用处:计算\(\frac{a}{b}mod\ P\)
#define LL long long
const int maxn = 3e5 + 5;
const int MOD = 1e9 + 7;
int inv[maxn];
LL quick_pow(LL a, LL b, LL mod) {
LL ans = 1;
while (b) {
if (b & 1) ans = ans * a % mod;
b >>= 1;
a = a * a % mod;
}
return ans % mod;
}
void exgcd(int a, int b, int &d, int &x, int &y) {
if (!b) {d = a; x = 1; y = 0; return ;}
else exgcd(b, a % b, d, y, x), y -= a / b * x;
}
int main() {
LL a;
LL G = quick_pow(a, MOD - 2, MOD); // a 在模MOD意义下逆元, a必须与MOD互质
LL x, y;
exgcd(a, MOD, x, y);
LL G = (x % MOD + MOD) % MOD // a 在模MOD意义下逆元,无特殊要求
LL n;
inv[1] = 1;
for (int i = 2; i <= n; i++) inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD;
// 线性求逆元
}
时间复杂度:前两种均为\(O(logn)\),最后一种为\(O(n)\)
质因数分解
用处:求一个数的质因数,欧拉函数值……
const int maxn = 3e5 + 5;
int p[maxn], c[maxn], cnt;
void divede(int n) {
for (int i = 2; i * i <= n; i++)
if (n % i == 0) {
p[++cnt] = i;
while (n % i == 0) c[i]++, n /= i;
}
if (n > 1) p[++cnt] = n, c[n] = 1;
}
int main() {
int n;
divide(n);
}
时间复杂度:\(O(\sqrt n)\)
线性筛法求素数及积性函数
线性筛素数
const int maxn = 3e5 + 5;
int prime[maxn], is_prime[maxn], cnt;
void euler_phi(int n) {
is_prime[1] = 1;
for (int i = 2; i <= n; i++) {
if (!is_prime[i]) prime[++cnt] = i;
for (int j = 1; j <= cnt && i * prime[j] <= n; j++) {
is_prime[i * prime[j]] = 1;
if (i % prime[j] == 0) break;
}
}
}
int main() {
euler_phi(maxn);
}
线性筛欧拉函数
const int maxn = 3e5 + 5;
int prime[maxn], phi[maxn], cnt;
void phi_table(int n) {
phi[1] = phi[0] = 0;
for (int i = 2; i <= n; i++) {
if (!phi[i])
prime[++cnt] = i, phi[i] = i - 1;
for (int j = 1; j <= cnt && i * prime[j] <= n; j++) {
if (i % prime[j] == 0) {
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
phi[i * prime[j]] = phi[i] * (prime[j] - 1);
}
}
}
int main() {
phi_table(maxn);
}
线性筛约数个数
const int maxn = 3e5 + 5;
int prime[maxn], is_prime[maxn], d[maxn], st[maxn], cnt;
void phi_table(int n) {
is_prime[1] = 1, d[1] = 1;
for (int i = 2; i <= n; i++) {
if (!is_prime[i])
prime[++cnt] = i, d[i] = 2, st[i] = 1;
for (int j = 1; j <= cnt && i * prime[j] <= n; j++) {
is_prime[i * prime[j]] = 1;
if (i % prime[j] == 0) {
d[i * prime[j]] = d[i] / (st[i] + 1) * (st[i] + 2);
st[i * prime[j]] = st[i] + 1; break;
}
d[i * prime[j]] = d[i] * d[prime[j]], st[i * prime[j]] = 1;
}
}
}
int main() {
phi_table(maxn);
}
线性筛约数和
const int maxn = 3e5 + 5;
int prime[maxn], is_prime[maxn], sd[maxn], st[maxn], cnt;
void phi_table(int n) {
is_prime[1] = 1, sd[1] = 1;
for (int i = 2; i <= n; i++) {
if (!is_prime[i])
prime[++cnt] = i, sd[i] = i + 1, st[i] = i + 1;
for (int j = 1; j <= cnt && i * prime[j] <= n; j++) {
is_prime[i * prime[j]] = 1;
if (i % prime[j] == 0) {
sd[i * prime[j]] = sd[i] / st[i] + 1 * (st[i] * prime[j] + 1);
st[i * prime[j]] = st[i] * prime[j] + 1; break;
}
sd[i * prime[j]] = sd[i] * sd[prime[j]], st[i * prime[j]] = prime[j] + 1;
}
}
}
int main() {
phi_table(maxn);
}
时间复杂度:以上代码复杂度均为\(O(n)\)
中国剩余定理
用处:求解模数互质的同余方程组
#define LL long long
const int maxn = 3e5 + 5;
LL a[maxn], m[maxn];
void exgcd(int a, int b, int &x, int &y) {
if (!b) {x = 1, y = 0; return ;}
else exgcd(b, a % b, y, x), y -= a / b * x;
}
LL China(int n, LL *a, LL*m) {
LL G = 1, ans = 0, x, y;
for (int i = 0; i < n; i++) G *= m[i];
for (int i = 0; i < n; i++) {
LL w = G / m[i];
exgcd(w, m[i], x, y);
x = (x % m[i] + m[i]) % m[i];
ans = (ans + x * w * a[i]) % G;
}
return (ans + G) % G;
}
int main() {
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++) scanf("%lld%lld", &a[i], &m[i]);
printf("%lld\n", China(n, a, m));
}
时间复杂度:\(O(nlogn)\)
整除分块
用处:求解类似于\(\sum_{i=1}^{n}f(i)\lfloor\frac{n}{i}\rfloor\)的式子,其中\(f(i)\)应该可以用前缀和预处理
#define LL long long
int main() {
for (LL l = 1, r; l <= n; l = r + 1) {
r = n / (n / l);
ans = (r - l + 1) * (n / l);
}
printf("%lld\n", ans);
}
时间复杂度:\(O(\sqrt n)\)
组合数
int quick_pow(int a, int b, int mod) {
int ans = 1;
while (b) {
if (b & 1) ans = ans * a % mod;
b >>= 1;
a = a * a % mod;
}
return ans % mod;
}
void init() {
fac[0] = 1;
for (int i = 1; i <= n; i++) fac[i] = fac[i - 1] * i % MOD;
for (int i = 1; i <= n; i++) ifac[i] = quick_pow(fac[i], MOD - 2, MOD);
}
int C(int n, int m) {
return fac[n] % MOD * ifac[m] % MOD * ifac[n - m] % MOD;
}
三·图论
图的存储与遍历
链式前向星
const int maxn = 3e5 + 5;
int n, m, tot, head[maxn];
struct Edge {
int to, val, nxt;
Edge(int _to, int _val, int _nxt) {
this -> to = _to;
this -> val = _val;
this -> nxt = _nxt;
} Edge(){}
}edge[maxn << 1];
void add(int from, int to, int val) {edge[++tot] = Edge(to, val, head[from]), head[from] = tot;}
void dfs(int u, int f) {
for (int i = head[u]; i; i = edge[i].nxt) {
int v = edge[i].to;
if (v == f) continue;
dfs(v, u);
}
}
时间复杂度:\(O(n)\)
vector
using std::pair;
using std::make_pair;
using std::vector;
#define pii pair<int, int>
const int maxn = 3e5 + 5;
vector<pii >G[maxn];
void add(int from, int to, int val) {
G[from].push_back(make_pair(to, val));
G[to].push_back(make_pair(from, val));
}
void dfs(int u, int f) {
for (int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
if (v == f) continue;
dfs(v, u);
}
}
时间复杂度:\(O(n)\),常数略大……
最短路算法
Floyd多源最短路
int g[1007][1007];
void init() {
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
if (i == j) g[i][j] = 0;
else g[i][j] = 0x3f3f3f3f;
for (int i = 1; i <= m; i++) {
int x, y, z;
read(x), read(y), read(z);
g[x][y] = g[y][x] = min(g[x][y], z);
}
}
void Floyd() {
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
g[i][j] = min(g[i][j], g[i][k] + g[k][j]);
}
时间复杂度:\(O(n^3)\)
Dijkstra单源最短路
using std::priority_queue;
const int maxn = 3e5 + 5;
int n, m, tot, head[maxn], dis[maxn], vis[maxn];
struct Edge {
int to, val, nxt;
Edge(int _to, int _val, int _nxt) {
this -> to = _to;
this -> val = _val;
this -> nxt = _nxt;
} Edge(){}
}edge[maxn << 1];
void add(int from, int to, int val) {edge[++tot] = Edge(to, val, head[from]), head[from] = tot;}
struct Node {
int w, now;
bool operator < (const Node &rhs) const {
return w > rhs.w;
}
};
void djikstra(int S) {
memset(dis, 0x3f, sizeof(dis));
memset(vis, 0, sizeof(0));
priority_queue<Node>pq;
pq.push((Node){0, S}), dis[S] = 0;
while (!pq.empty()) {
Node x = pq.top(); pq.pop();
int u = x.now();
if (vis[u]) continue;
vis[u] = 1;
for (int i = head[u]; i; i = edge[i].nxt) {
int v = edge[i].to;
if (dis[v] > dis[u] + edge[i].val) {
dis[v] = dis[u] + edge[i].val;
pq.push((Node){dis[v], v});
}
}
}
}
Spfa单源最短路
using std::queue;
const int maxn = 3e5 + 5;
int n, m, tot, head[maxn], dis[maxn], vis[maxn];
struct Edge {
int to, val, nxt;
Edge(int _to, int _val, int _nxt) {
this -> to = _to;
this -> val = _val;
this -> nxt = _nxt;
} Edge(){}
}edge[maxn << 1];
void add(int from, int to, int val) {edge[++tot] = Edge(to, val, head[from]), head[from] = tot;}
void spfa(int S) {
memset(dis, 0x3f, sizeof(dis));
memset(vis, 0, sizeof(vis));
queue<int>q;
q.push(S), dis[S] = 0, vis[S] = 1;
while (!q.empty()) {
int u = q.front();
q.pop(); vis[u] = 0;
for (int i = head[u]; i; i = edge[i].nxt) {
int v = edge[i].to;
if (dis[v] > dis[u] + edge[i].val) {
dis[v] = dis[u] + edge[i].val;
if (!vis[v]) {
vis[v] = 1;
q.push(v);
}
}
}
}
}
并查集
严格讲貌似不属于图论,不过就在这里放着吧……
int fa[maxn];
struct DSU {
int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}
bool judge(int x, int y) {return find(x) == find(y);}
void merge(int x. int y) {fa[find(x)] = find(y);}
}T;
void init() {
for (int i = 1; i <= n; i++) fa[i] = i;
......
}
时间复杂度:\(O(nlogn)\)
Kruskal最小生成树算法
用处:求最小生成树
struct Edge {
int from, to, val;
bool operator < (const Edge &rhs) const {
return val < rhs.val;
}
}edge[maxn];
int fa[maxn];
struct DSU {
int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}
bool judge(int x, int y) {return find(x) == find(y);}
void merge(int x. int y) {fa[find(x)] = find(y);}
}T;
int main() {
read(n), read(m);
for (int i = 1; i <= m; i++) read(edge[i].from), read(edge[i].to), read(edge[i].val);
std::sort(edge + 1, edge + m + 1);
for (int i = 1; i <= n; i++) fa[i] = i;
int sum = 0, cnt = 0;
for (int i = 1; i <= m; i++) {
int x = edge[i].from, y = edge[i].to;
if (T.judge(x, y)) continue;
T.merge(x, y);
sum += edge[i].val;
++cnt;
if (cnt == n - 1) break;
}
printf("%d\n", sum);
}
最近公共祖先
倍增LCA
const int maxn = 3e5 + 5;
int n, m, tot, head[maxn], dep[maxn], fa[maxn][22], lg[maxn];
struct Edge {
int to, nxt;
Edge (int _to, int _nxt) {
this -> to = _to;
this -> nxt = _nxt;
} Edge(){}
}edge[maxn << 1];
void add(int from, int to) {edge[++tot] = Edge(to, head[from]), head[from] = tot;}
void dfs(int u, int f) {
dep[u] = dep[f] + 1;
fa[u][0] = f;
for (int i = 1; (1 << i) <= dep[u]; i++)
fa[u][i] = fa[fa[u][i - 1]][i - 1];
for (int i = head[u]; i; i = edge[i].nxt) {
int v = edge[i].to;
if (v == f) continue;
dfs(v, u);
}
}
int query(int x, int y) {
if (dep[x] < dep[y]) std::swap(x, y);
while (dep[x] > dep[y])
x = fa[x][lg[dep[x] - dep[y]] - 1];
if (x == y) return x;
for (int j = lg[dep[x]] - 1; j >= 0; j--)
if (fa[x][j] != fa[y][j])
x = fa[x][j], y = fa[y][j];
return fa[x][0];
}
int main() {
for (int i = 1; i <= n; i++) lg[i] = lg[i - 1] + ((1 << lg[i - 1]) == i);
......
}
时间复杂度:\(O(nlogn)\)
树剖LCA
const int maxn = 3e5 + 5;
int n, m, tot, head[maxn], dep[maxn], siz[maxn], fa[maxn], son[maxn], top[maxn];
struct Edge {
int to, nxt;
Edge (int _to, int _nxt) {
this -> to = _to;
this -> nxt = _nxt;
} Edge(){}
}edge[maxn << 1];
void add(int from, int to) {edge[++tot] = Edge(to, head[from]), head[from] = tot;}
void dfs1(int u, int f, int deep) {
dep[u] = deep, fa[u] = f, siz[u] = 1;
for (int i = head[u]; i; i = edge[i].nxt) {
int v = edge[i].to;
if (v == f) continue;
dfs1(v, u, deep + 1);
siz[u] += siz[v];
if (siz[v] > siz[son[u]]) son[u] = v;
}
}
void dfs2(int u, int topf) {
top[u] = topf;
if (!son[u]) return ;
dfs2(son[u], topf);
for (int i = head[u]; i; i = edge[i].nxt) {
int v = edge[i].to;
if (v == fa[u] || v == son[u]) continue;
dfs2(v, v);
}
}
int query(int x, int y) {
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) std::swap(x, y);
x = fa[top[x]];
}
return dep[x] < dep[y] ? x : y;
}
int main() {
......
dfs1(1, 0, 1);
dfs2(1, 1);
......
}
Tarjan 算法
割点/割顶
const int maxn = 3e5 + 5;
int n, m, tot, num, head[maxn], dfn[maxn], low[maxn], cnt[maxn];
struct Edge {
int to, nxt;
Edge (int _to, int _nxt) {
this -> to = _to;
this -> nxt = _nxt;
} Edge(){}
}edge[maxn << 1];
void add(int from, int to) {edge[++tot] = Edge(to, head[from]), head[from] = tot;}
void tarjan(int u, int f) {
dfn[u] = low[u] = ++num;
int flag = 0;
for (int i = head[u]; i; i = edge[i].nxt) {
int v = edge[i].to;
if (!dfn[v]) {
tarjan(v, fa);
low[u] = min(low[u], low[v]);
if (low[v] >= dfn[u] && x != fa) cnt[u] = 1;
if (x == fa) flag++;
}
low[u] = min(low[u], dfn[v]);
}
if (x == fa && flag >= 2) cnt[u] = 1;
}
割边
const int maxn = 3e5 + 5;
int n, m, tot, num, head[maxn], dfn[maxn], low[maxn], bridge[maxn];
struct Edge {
int to, nxt;
Edge (int _to, int _nxt) {
this -> to = _to;
this -> nxt = _nxt;
} Edge(){}
}edge[maxn << 1];
void add(int from, int to) {edge[++tot] = Edge(to, head[from]), head[from] = tot;}
void tarjan(int u, int in_edge) {
dfn[u] = low[u] = ++num;
for (int i = head[u]; i; i = edge[i].nxt) {
int v = edge[i].to;
if (!dfn[v]) {
tarjan(v, i);
low[u] = min(low[u], low[v]);
if (low[v] > dfn[u]) bridge[i] = bridge[i ^ 1] = 1;
}
else if (i != (in_edge ^ 1))
low[u] = min(low[u], dfn[v]);
}
}
int main() {
tot = 1;
......
}
强连通分量
const int maxn = 3e5 + 5;
int n, m, tot, num, num_node, head[maxn], dfn[maxn], low[maxn], st[maxn], belong[maxn], cnt, vis[maxn];
struct Edge {
int to, nxt;
Edge (int _to, int _nxt) {
this -> to = _to;
this -> nxt = _nxt;
} Edge(){}
}edge[maxn << 1];
void add(int from, int to) {edge[++tot] = Edge(to, head[from]), head[from] = tot;}
void tarjan(int u, int in_edge) {
dfn[u] = low[u] = ++num;
st[++cnt] = u; vis[u] = 1;
for (int i = head[u]; i; i = edge[i].nxt) {
int v = edge[i].to;
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]) {
int y;
++num_node;
while (y = st[cnt--]) {
belong[y] = num_node;
vis[y] = false;
if (u == y) break;
}
}
}
int main() {
......
}
时间复杂度:上述代码均为\(O(n)\)
二分图最大匹配
匈牙利算法
用处:据长者说,\(CSP\)可能要考……
#include <iostream>
#include <cstdio>
#include <cstring>
bool vis[1005];
int n, m, e, a[1005][1005], belong[1005];
template<class T>
inline T read(T &x) {
x = 0; int w = 1, ch = getchar();
for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') w = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - 48;
return x *= w;
}
bool judge(int x) {
for (int i = 1; i <= m; i++)
if (a[x][i] && !vis[i]) {
vis[i] = 1;
if (!belong[i] || judge(belong[i])) {
belong[i] = x;
return true;
}
}
return false;
}
int main() {
read(n), read(m), read(e);
for (int i = 1; i <= e; i++) {
int x, y;
read(x), read(y);
a[x][y] = 1;
}
int ans = 0;
for (int i = 1; i <= n; i++) {
memset(vis, false, sizeof(vis));
if (judge(i)) ++ans;
}
std::cout << ans << '\n';
return 0;
}
时间复杂度:\(O(n^2m)\)
Dinic实现二分图匹配
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define min(a, b) ((a) < (b) ? (a) : (b))
const int maxn = 1e6 +5;
const int INF = 1e9;
int n, m, e, head[maxn], tot = -1, s, t;
int maxflow, dep[maxn];
std::queue<int>q;
template<class T>
inline T read(T &x) {
x = 0; int w = 1, ch = getchar();
for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') w = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - 48;
return x *= w;
}
struct Edge {
int to, val, nxt;
Edge(int y, int w, int next) {
to = y, val = w, nxt = next;
}
Edge(){
}
}edge[maxn << 1];
void add(int from, int to, int val)
{
edge[++tot] = Edge(to, val, head[from]); head[from] = tot;
}
bool bfs(int s, int t)
{
memset(dep, 0x7f, sizeof(dep));
while (!q.empty()) q.pop();
dep[s] = 0;
q.push(s);
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = head[u]; i != -1; i = edge[i].nxt) {
int v = edge[i].to;
if (dep[v] > INF && edge[i].val) {
dep[v] = dep[u] + 1;
q.push(v);
if (v == t) return true;
}
}
}
return false;
}
int dfs(int u, int t, int limit)
{
if (!limit || u == t) return limit;
int flow = 0, f;
for (int i = head[u]; i != -1; i = edge[i].nxt) {
int v = edge[i].to;
if (dep[v] == dep[u] + 1 && edge[i].val) {
f = dfs(v, t, min(limit, edge[i].val));
if (!f) dep[v] = 0;
flow += f;
limit -= f;
edge[i].val -= f;
edge[i ^ 1].val += f;
if (!limit) break;
}
}
return flow;
}
void dinic(int s, int t)
{
while (bfs(s, t))
maxflow += dfs(s, t, INF);
}
int main()
{
memset(head, -1, sizeof(head));
int x, y;
read(n), read(m), read(e);
s = n + m + 2, t = n + m + 3;
for (int i = 1; i <= e; i++) {
read(x), read(y);
if (x > n || y > m) continue;
add(x, y + n, 1);
add(y + n, x, 0);
}
for (int i = 1; i <= n; i++)
add(s, i, 1), add(i, s, 0);
for (int i = 1; i <= m; i++)
add(i + n, t, 1), add(t, i + n, 0);
dinic(s, t);
printf("%d\n", maxflow);
return 0;
}
时间复杂度:\(O(n\sqrt m)\)
四·动态规划
最长上升子序列LIS
const int INF = 1e9;
int d[100005];
void solve() {
d[0] = -INF;
for (int i = 1; i <= n; i++) d[i] = INF;
int ans = 0;
for (int i = 1; i <= n; i++) {
int pos = std::lower_boud(d, d + i, a[i]) - d;
d[pos] = std::min(d[pos], a[i]);
ans = std::max(ans, pos);
}
}
时间复杂度:\(O(nlogn)\)
最长公共子序列LCS
const int INF = 1e9;
int a[1001], b[1001], f[1001][1001];
void solve() {
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++) scanf("%d", &b[i]);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) {
if (a[i] == b[j]) f[i][j] = max(f[i][j], f[i - 1][j - 1] + 1);
else f[i][j] = max(f[i - 1][j], f[i][j - 1]);
}
ans : f[n][n]
}
时间复杂度:\(O(n^2)\)
背包类问题
01背包
const int INF = 1e9;
int w[1001], v[1001], f[10001];
void solve() {
for (int i = 1; i <= n; i++)
scanf("%d%d", &w[i], &v[i]);
for (int i = 1; i <= n; i++)
for (int j = V; j >= w[i]; j--)
f[i] = std::max(f[i], f[j - w[i]] + v[i]);
}
时间复杂度:\(O(nV)\)
完全背包
const int INF = 1e9;
int w[1001], v[1001], f[10001];
void solve() {
for (int i = 1; i <= n; i++)
scanf("%d%d", &w[i], &v[i]);
for (int i = 1; i <= n; i++)
for (int j = w[i]; j <= V; j++)
f[i] = std::max(f[i], f[j - w[i]] + v[i]);
}
二进制优化多重背包
const int INF = 1e9;
int w, v, k, f[10001];
void solve() {
while (n--) {
scanf("%d%d%d", &w, &v, &k);
for (int d = 1; d < k; k -= d; d <<= 1)
for (int i = V; i >= d * w; i--)
f[i] = std::max(f[i], f[i - w * d] + d * v);
for (int i = V; i >= k * w; k--)
f[i] = std::max(f[i], f[i - w * k] + k * v);
}
}
时间复杂度:\(O(nVlogK)\)
二维费用背包
#include <iostream>
#include <cstdio>
#include <cstring>
int f[1007][1007], a[1007], b[1007], c[1007], n, m, x;
int main()
{
ios::sync_with_stdio(0);
cin >> n >> m >> x;
for (int i = 1; i <= n; i++) cin >> a[i] >> b[i] >> c[i];
for (int i = 1; i <= n; i++)
for (int j = m; j >= b[i]; j--)
for (int v = x; v >= c[i]; v--)
f[j][v] = std::max(f[j][v], f[j - b[i]][v - c[i]] + a[i]);
cout << f[m][x] << '\n';
return 0;
}
时间复杂度:\(O(nmx)\)
树形动规问题
#include <iostream>
#include <cstdio>
#include <cstring>
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
#define rint register int
#define LL long long
const int maxn = 1e5 + 5;
const int MOD = 1e9 + 7;
int n, k, tot, head[maxn], f[maxn][105][4], siz[maxn], g[105][4];
/*
0 -> 安装且被监听
1 -> 安装且不被监听
2 -> 不安装被监听
3 -> 不安装不被监听
*/
struct Edge {
int to, nxt;
Edge(int _to, int _nxt) {
this -> to = _to;
this -> nxt = _nxt;
} Edge(){}
}edge[maxn << 1];
void add(int from, int to) {edge[++tot] = Edge(to, head[from]), head[from] = tot;}
template<class T>
inline T read(T &x) {
x = 0; int w = 1, ch = getchar();
for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') w = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - 48;
return x *= w;
}
void dfs(int u, int fa) {
f[u][1][1] = 1; f[u][0][3] = 1; siz[u] = 1;
for (rint i = head[u]; i; i = edge[i].nxt) {
int v = edge[i].to;
if (v == fa) continue;
dfs(v, u);
siz[u] += siz[v]; memcpy(g, f[u], sizeof(g));
for (rint j = min(siz[u], k); ~j; j--) f[u][j][0] = f[u][j][1] = f[u][j][2] = f[u][j][3] = 0;
for (rint j = min(siz[u], k); ~j; j--) {
for (rint p = max(0, j + siz[v] - siz[u]); p <= min(siz[v], j); p++) {
f[u][j][3] += ((LL)f[v][p][2] * g[j - p][3]) % MOD, f[u][j][3] %= MOD;
f[u][j][2] += ((LL)(f[v][p][0] % MOD + f[v][p][2] % MOD) % MOD) *
((g[j - p][2] % MOD + g[j - p][3] % MOD) % MOD) % MOD;
f[u][j][2] %= MOD;
f[u][j][1] += ((LL)(f[v][p][2] % MOD + f[v][p][3] % MOD)) % MOD *
g[j - p][1] % MOD; f[u][j][1] %= MOD;
f[u][j][0] += ((LL)(f[v][p][0] + f[v][p][1] % MOD) + (f[v][p][2] + f[v][p][3]) % MOD)
% MOD * ((g[j - p][1] % MOD+ g[j - p][0] % MOD)) % MOD; f[u][j][0] %= MOD;
}
f[u][j][0] = (f[u][j][0] - f[u][j][1] + MOD) % MOD;
f[u][j][2] = (f[u][j][2] - f[u][j][3] + MOD) % MOD;
}
}
}
int main() {
int x, y;
read(n), read(k);
for (int i = 1; i < n; i++) {
read(x), read(y);
add(x, y), add(y, x);
}
dfs(1, 0);
std::cout << f[1][k][0] + f[1][k][2]<< '\n';
return 0;
}
时间复杂度:树形动规一般情况下为\(O(n)\),树形背包为严格\(O(n^2)\)
数位DP
#include <iostream>
#include <cstdio>
#include <cstring>
#define int long long
const int maxn = 25;
const int MOD = 1e9 + 7;
int T, L, R;
int val[maxn], f[maxn][maxn];
template<class T>
inline T read(T &x) {
x = 0; int w = 1, ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') w = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - 48; ch = getchar();}
return x *= w;
}
int dfs(int pos, int limit, int lead_zero, int k, int sum) {
if (!pos) return sum;
if (!limit && !lead_zero && f[pos][sum] != -1)
return f[pos][sum];
int lim = limit ? val[pos] : 9;
int ans = 0;
for (int i = 0; i <= lim; i++) {
if (lead_zero && !i)
ans += dfs(pos - 1, limit && (i == lim), 1, k, sum);
else
ans += dfs(pos - 1, limit && (i == lim), 0, k, sum + (i == k));
}
if (!limit && !lead_zero)
f[pos][sum] = ans;
return ans;
}
int solve(int n, int k) {
memset(f, -1, sizeof(f));
int len = 0;
while (n) val[++len] = n % 10, n /= 10;
return dfs(len, 1, 1, k, 0);
}
signed main() {
read(T);
while (T--) {
int ans = 0;
read(L), read(R);
for (int i = 1; i <= 9; i++)
ans += (((solve(R, i) - solve(L - 1, i) + MOD) % MOD) * i % MOD + MOD) % MOD, ans %= MOD;
printf("%lld\n", ans);
}
return 0;
}
时间复杂度:\(O(\)玄学不会证\()\)
状态压缩DP
UVA11008 【Antimatter Ray Clearcutting】
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 70000;
int T, n, m, x[20], y[20], g[20][20]; //x,y储存坐标,g用于储存直线
int f[maxn], N;
// f用于记忆化,N为初始状态。
void init()
{
memset(f, -1, sizeof(f));
memset(g, 0, sizeof(g)); //多测不清空,爆零两行泪!!!
scanf("%d%d", &n, &m);
N = (1 << n) - 1; //搜索初始状态
for (int i = 0; i < n; i++)
scanf("%d%d", &x[i], &y[i]);
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (i == j) continue;
for (int k = n - 1; k >= 0; k--) {
g[i][j] <<= 1; //左移一位,为下一个点腾出位置
if ((x[j] - x[i]) * (y[k] - y[j]) == (y[j] - y[i]) * (x[k] - x[j]))
g[i][j]++; //表示这个点在这条直线上
}
}
}
}
int Count(int k)
{
int cnt = 0;
for (int i = 0; i < n; i++)
if ((1 << i) & k) cnt++;
return cnt;
}
int dfs(int now)
{
int cnt = Count(now);
// Count用于计算还有多少个点没删
int& ans = f[now];
// 这个取地址符一定要加!! 原因是后面f[now]也要改变
if (cnt <= n - m) return ans = 0;
// 如果剩下的点小于n-m,那么就不用再删了,直接返回0
else if (cnt == 1) return ans = 1;
//如果还剩一个点,直接用一条直线来删
else if (ans > -1) return ans;
//记忆化,不解释
ans = (1 << 30);
//都没有,枚举直线更新状态
for (int i = 0; i < n; i++) {
if ((1 << i) & now) {
// 保证i这个点还没有被删
for (int j = i + 1; j < n; j++) {
if ((1 << j) & now) {
//保证j这个点没被删
int temp = now & (g[i][j] ^ N); //异或取补集,再取and,得到下一个状态
ans = min(ans, dfs(temp) + 1); //更新ans
}
}
}
}
return ans;
}
int main()
{
int t = 1;
scanf("%d", &T);
while (T--) {
init(); //初始化
printf("Case #%d:\n%d\n", t++, dfs(N));
if (T) puts("");
}
return 0;
}
时间复杂度:一般为指数级貌似是废话
五·数据结构
线段树
用处:不说了,大家都知道……
#include <iostream>
#include <cstdio>
#include <cstring>
#define int long long
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
const int maxn = 1e5 + 5;
int n, m, a[maxn], opt, l, r, v;
struct Node {
int sum, tag;
}z[maxn << 2];
void build(int rt, int l, int r) {
if (l == r) {
z[rt].sum = a[l];
return ;
}
int mid = (l + r) >> 1;
build(rt << 1, l, mid);
build(rt << 1 | 1, mid + 1, r);
z[rt].sum = z[rt << 1].sum + z[rt << 1 | 1].sum;
}
void modify(int rt, int l, int r, int x, int y, int v) {
z[rt].sum += (min(r, y) - max(l, x) + 1) * v;
if (x <= l && r <= y) {
z[rt].tag += v;
return ;
}
int mid = (l + r) >> 1;
if (x <= mid) modify(rt << 1, l, mid, x, y, v);
if (y > mid) modify(rt << 1 | 1, mid + 1, r, x, y, v);
}
int query(int rt, int l, int r, int x, int y, int tg) {
if (x <= l && r <= y) {
return z[rt].sum + (min(r, y) - max(l, x) + 1) * tg;
}
int mid = (l + r) >> 1;
int ret = 0;
if (x <= mid) ret += query(rt << 1, l, mid, x, y, tg + z[rt].tag);
if (y > mid) ret += query(rt << 1 | 1, mid + 1, r, x, y, tg + z[rt].tag);
return ret;
}
signed main() {
scanf("%lld%lld", &n, &m);
for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
build(1, 1, n);
for (int i = 1; i <= m; i++) {
scanf("%lld", &opt);
if (opt == 1) {
scanf("%lld%lld%lld", &l, &r, &v);
modify(1, 1, n, l, r, v);
}
else {
scanf("%lld%lld", &l, &r);
printf("%lld\n", query(1, 1, n, l, r, 0));
}
}
return 0;
}
时间复杂度:\(O(nlogn)\)
树状数组
用处:大家都知道……
#include <iostream>
#include <cstdio>
#include <cstring>
#define int long long
#define lowbit(x) ((x) & -(x))
const int maxn = 5e5 + 5;
int n, m;
template<class T>
inline T read(T &x) {
x = 0; int w = 1, ch = getchar();
for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') w = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - 48;
return x *= w;
}
struct FenwickTree {
int bit[maxn];
void add(int x, int v) {for (; x <= n; x += lowbit(x)) bit[x] += v;}
int query(int x) {int ret = 0; for (; x; x -= lowbit(x)) ret += bit[x]; return ret;}
}T;
signed main() {
read(n), read(m);
for (int i = 1; i <= n; i++) {
int temp; read(temp);
T.add(i, temp);
}
int opt, x, y;
for (; m; m--) {
read(opt), read(x), read(y);
if (opt == 1) T.add(x, y);
else printf("%lld\n", T.query(y) - T.query(x - 1));
}
return 0;
}
时间复杂度:\(O(nlogn)\)
ST表
用处:………………
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
const int maxn = 1e6 + 5;
int n, m, f[maxn][22];
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &f[i][0]);
for (int j = 1; (1 << j) <= n; j++)
for (int i = n; i - (1 << j) + 1 >= 1; i--)
f[i][j] = std::max(f[i][j - 1], f[i - (1 << (j - 1))][j - 1]);
for (; m; m--) {
int l, r;
scanf("%d%d", &l, &r);
int j = log2(r - l + 1);
int ans = std::max(f[r][j], f[l + (1 << j) - 1][j]);
printf("%d\n", ans);
}
return 0;
}
时间复杂度:预处理\(O(nlogn)\),查询\(O(1)\)
主席树
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
const int maxn = 2e5 + 5;
int n, m, a[maxn], b[maxn];
struct Node {int l, r, sum; Node(){l = r = sum = 0;}}z[maxn << 5];
int cnt, root[maxn];
template<class T>
inline T read(T &x) {
x = 0; int w = 1, ch = getchar();
for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') w = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - 48;
return x *= w;
}
void build(int &rt, int l, int r) {
rt = ++cnt; if (l == r) return ;
int mid = (l + r) >> 1;
build(z[rt].l, l, mid), build(z[rt].r, mid + 1, r);
}
void modify(int &rt, int pre, int l, int r, int pos) {
rt = ++cnt;
z[rt].l = z[pre].l, z[rt].r = z[pre].r, z[rt].sum = z[pre].sum + 1;
if (l == r) return ; int mid = (l + r) >> 1;
if (pos <= mid) modify(z[rt].l, z[pre].l, l, mid, pos);
else modify(z[rt].r, z[pre].r, mid + 1, r, pos);
}
int query(int L, int R, int l, int r, int pos) {
if (l == r) return l; int mid = (l + r) >> 1;
int temp = z[z[R].l].sum - z[z[L].l].sum;
if (pos <= temp) return query(z[L].l, z[R].l, l, mid, pos);
else return query(z[L].r, z[R].r, mid + 1, r, pos - temp);
}
int main() {
read(n), read(m); int x, y, k;
for (int i = 1; i <= n; i++) b[i] = read(a[i]);
std::sort(b + 1, b + n + 1);
int len = std::unique(b + 1, b + n + 1) - b - 1;
build(root[0], 1, len);
for (int i = 1; i <= n; i++) {
int pos = std::lower_bound(b + 1, b + len + 1, a[i]) - b;
modify(root[i], root[i - 1], 1, len, pos);
}
for (int i = 1; i <= m; i++) {
read(x), read(y), read(k);
printf("%d\n", b[query(root[x - 1], root[y], 1, len, k)]);
}
return 0;
}
时间复杂度:\(O(nlogn)\)
FHQ Treap
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#define pii pair<int, int>
using namespace std;
const int maxn = 1e5 + 5;
int w, ch;
struct Node {
int key, val;
int ls, rs, size;
}g[maxn];
int root, cnt;
template<class T>
T read(T &x)
{
x = 0, w = 1, ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') w = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - 48; ch = getchar();}
return x *= w;
}
void pushup(int p)
{
g[p].size = g[g[p].ls].size + g[g[p].rs].size + 1;
}
void insert(int x)
{
++cnt;
g[cnt].val = x;
g[cnt].size = 1;
g[cnt].key = rand();
g[cnt].ls = g[cnt].rs = 0;
}
int merge(int x, int y)
{
if (x == 0) return y;
if (y == 0) return x;
if (g[x].key < g[y].key) {
g[x].rs = merge(g[x].rs, y);
pushup(x);
return x;
}
else {
g[y].ls = merge(x, g[y].ls);
pushup(y);
return y;
}
}
void split(int u, int x, int &l, int &r)
{
if (!u) {l = r = 0; return;}
if (g[u].val <= x) {l = u; split(g[u].rs, x ,g[u].rs, r);}
else {r = u; split(g[u].ls, x, l, g[u].ls);}
pushup(u);
}
int rnk(int u, int num){
if (num <= g[g[u].ls].size) {
int ret = rnk(g[u].ls, num);
return ret;
}
else{
if (num == g[g[u].ls].size + 1) return u;
else {
num -= g[g[u].ls].size + 1;
return rnk(g[u].rs, num);
}
}
}
int main()
{
int n, temp, x, l, r;
read(n);
for (int i = 1; i <= n; i++) {
read(temp), read(x);
switch (temp) {
case 1:
split(root, x, l, r);
insert(x);
root = merge(merge(l, cnt), r);
break;
case 2:
int temp;
split(root, x, l, temp);
split(l, x - 1, l, r);;
r = merge(g[r].ls, g[r].rs);
root = merge(merge(l, r), temp);
break;
case 3:
split(root, x - 1, l, r);
printf("%d\n", g[l].size + 1);
root = merge(l, r);
break;
case 4:
printf("%d\n", g[rnk(root, x)].val);
break;
case 5:
split(root, x - 1, l, r);
printf("%d\n", g[rnk(l, g[l].size)].val);
root = merge(l, r);
break;
case 6:
split(root, x, l, r);
printf("%d\n", g[rnk(r, 1)].val);
root = merge(l, r);
break;
}
}
return 0;
}
时间复杂度:\(O(nlogn)\)
六·字符串算法
KMP字符串匹配算法
#include <iostream>
#include <cstdio>
#include <cstring>
const int maxn = 1e6 + 6;
int kmp[maxn], j;
char a[maxn], b[maxn];
int main() {
scanf("%s", a + 1); scanf("%s", b + 1);
int lena = strlen(a + 1), lenb = strlen(b + 1);
for (int i = 2; i <= lenb; i++) {
while (j && b[i] != b[j + 1])
j = kmp[j];
if (b[i] == b[j + 1]) j++;
kmp[i] = j;
}
j = 0;
for (int i = 1; i <= lena; i++) {
while (j && a[i] != b[j + 1])
j = kmp[j];
if (a[i] == b[j + 1]) j++;
if (j == lenb) {printf("%d\n", i - lenb + 1); j = kmp[j];}
}
for (int i = 1; i <= lenb; i++) printf("%d ", kmp[i]); puts("");
return 0;
}
时间复杂度:\(O(n+m)\)
Manacher算法
#include <iostream>
#include <cstdio>
#include <cstring>
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
using std::string;
const int maxn = 11000002;
string x, s;
int radius[maxn << 1], max_right, mid, ans;
int main() {
std::cin >> x;
s += '&', s += '$';
for (int i = 0; i < x.length(); i++) s += x[i], s += '$';
for (int i = 1; i < s.length(); i++) {
radius[i] = max_right > i ? min(radius[mid * 2 - i], max_right - i) : 1;
while (s[i - radius[i]] == s[i + radius[i]]) radius[i]++;
if (i + radius[i] > max_right) max_right = i + radius[i], mid = i;
ans = max(ans, radius[i] - 1);
}
printf("%d\n", ans);
return 0;
}
时间复杂度:\(O(n)\)
Trie树
#include <iostream>
#include <cstdio>
#include <cstring>
#define int long long
const int maxn = 5e5 + 5;
int n, m;
struct Trie {
int ch[maxn][26], siz;
bool vis[maxn];
Trie() {
siz = 1;
memset(ch[0], 0, sizeof(ch[0]));
memset(vis, false, sizeof(vis));
}
void insert(char *s) {
int root = 0, len = strlen(s + 1);
for (int i = 1; i <= len; i++) {
int ooo = s[i] - 'a';
if (!ch[root][ooo]) {
memset(ch[siz], 0, sizeof(ch[siz]));
ch[root][ooo] = siz++;
}
root = ch[root][ooo];
}
}
int search(char *s) {
int root = 0, len = strlen(s + 1);
for (int i = 1; i <= len; i++) {
int ooo = s[i] - 'a';
if (!ch[root][ooo]) return 0;
root = ch[root][ooo];
}
if (!vis[root]) {
vis[root] = true;
return 1;
}
return 2;
}
}trie;
signed main() {
scanf("%lld", &n);
char s[100];
for (int i = 1; i <= n; i++) {
scanf("%s", s + 1);
trie.insert(s);
}
scanf("%lld", &m);
for (int i = 1; i <= m; i++) {
scanf("%s", s + 1);
int ankh = trie.search(s);
if (ankh == 0) puts("WRONG");
else if (ankh == 1) puts("OK");
else puts("REPEAT");
}
return 0;
}
总时间复杂度:\(O(n * len)\)
七·其它
朴素高精度
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
using namespace std;
const int maxn = 2005;
struct BigNum {
int d[maxn], len;
void clean() {while (len > 1 && !d[len - 1]) len--;}
BigNum() {memset(d, 0, sizeof(d)); len = 1;}
BigNum(int num) {*this = num;}
BigNum(char *num) {*this = num;}
BigNum operator = (const char *num) {
memset(d, 0, sizeof(d)), len = strlen(num);
for (int i = 0; i < len; i++) d[i] = num[len - i - 1] - '0';
clean();
return *this;
}
BigNum operator = (int num) {
char s[20]; sprintf(s, "%d", num);
*this = s;
return *this;
}
BigNum operator + (const BigNum &b) {
BigNum c = *this; int i;
for (i = 0; i < b.len; i++) {
c.d[i] += b.d[i];
if (c.d[i] > 9) c.d[i] %= 10, c.d[i + 1]++;
}
while (c.d[i] > 9) c.d[i++] %= 10, c.d[i]++;
c.len = max(len, b.len);
if (c.d[i] && c.len <= i) c.len = i + 1;
return c;
}
BigNum operator - (const BigNum &b) {
BigNum c = *this; int i;
for (i = 0 ; i < b.len; i++) {
c.d[i] -= b.d[i];
if (c.d[i] < 0) c.d[i] += 10, c.d[i + 1]--;
}
while (c.d[i] < 0) c.d[i++] += 10, c.d[i]--;
c.clean();
return c;
}
BigNum operator * (const BigNum &b) const {
BigNum c; int i, j; c.len = (len + b.len);
for (j = 0 ; j < b.len; j++)
for (i = 0; i < len; i++)
c.d[i + j] = d[i] * b.d[j];
for (i = 0; i < c.len - 1; i++)
c.d[i + 1] += c.d[i] / 10, c.d[i] %= 10;
c.clean();
return c;
}
BigNum operator / (const BigNum &b) {
int i, j;
BigNum c = *this, last = 0;
for (i = len - 1; i >= 0; i--) {
last = last * 10 + d[i];
for (j = 0 ; j < 10; j++) if (last < b * (j + 1)) break;
c.d[i] = j;
last = last - b * j;
}
c.clean();
return c;
}
BigNum operator % (const BigNum &b) {
int i, j;
BigNum last = 0;
for (i = len - 1; i >= 0; i--) {
last = last * 10 + d[i];
for (j = 0; j < 10; j++) if (last < b * (j + 1)) break;
last = last - b * j;
}
return last;
}
bool operator < (const BigNum &b) const {
if (len != b.len) return len < b.len;
for (int i = len - 1; i >= 0; i--)
if (d[i] != b.d[i]) return d[i] < b.d[i];
return false;
}
string str() const {
char s[maxn] = {};
for (int i = 0; i < len; i++) s[len - i - 1] = d[i] + '0';
return s;
}
};
istream& operator >> (istream& in, BigNum &x) {
string s;
in >> s;
x = s.c_str();
return in;
}
ostream& operator << (ostream& out, const BigNum &x) {
out << x.str();
return out;
}
int main() {
BigNum a, b;
cin >> a >> b;
cout << a + b << '\n';
return 0;
}
压位高精度
// 受rainy大佬所教
const int_t base = 100000000;
void print(int_t x, int_t dig) {
if(!dig) return;
print(x / 10, dig - 1);
putchar(x % 10 + '0');
}
sturct BigInt {
int_t num[10], siz;
BigInt():siz(0){memset(num, 0, sizeof num);}
void operator !(){for(int_t i=0;i<siz;i++)if(num[i]>=base)siz=max(siz,i+2),num[i+1]+=num[i]/base,num[i]%=base;}
void operator +=(int_t x){num[0]+=x;!*this;}
void operator *=(int_t x){for(int_t i=0;i<siz;i++)num[i]*=x;!*this;}
void operator +=(BigInt x){siz=x.siz=max(siz, x.siz);for(int_t i=0;i<siz;i++)num[i]+=x[i];!*this;}
void operator ~(){printf("%lld",num[siz-1]);for(int_t i=siz-2;~i;i--)print(num[i],8);}
};
BigInt read() {
BigInt ans;
while(!isdigit(ch)) ch = getchar();
while(isdigit(ch)) ans *= 10, ans += ch - '0', ch = getch();
return ans;
}
int main() {
BigInt a; a += 100000000;
a *= 123123123;
~a;
}
矩阵快速幂加速递推
#include <iostream>
#include <cstdio>
#include <cstring>
#define int long long
using namespace std;
const int MOD = 1e9 + 7;
int n;
struct Matrix {
int a[4][4];
Matrix(){memset(a, 0, sizeof(a));}
}ans, base;
Matrix operator * (const Matrix &x, const Matrix &y)
{
Matrix ret;
for (int i = 1; i <= 2; i++)
for (int j = 1; j <= 2; j++)
for (int k = 1; k <= 2; k++)
ret.a[i][j] = (ret.a[i][j] + x.a[i][k] * y.a[k][j]) % MOD;
return ret;
}
void init()
{
base.a[1][1] = base.a[1][2] = base.a[2][1] = 1;
base.a[2][2] = 0;
ans.a[1][1] = ans.a[1][2] = 1;
}
void quick_pow(int b)
{
while (b) {
if (b & 1) ans = ans * base;
base = base * base;
b >>= 1;
}
}
signed main()
{
init();
scanf("%lld", &n);
if (n <= 2) {puts("1"); return 0;}
quick_pow(n - 2);
printf("%lld\n", ans.a[1][1]);
return 0;
}
时间复杂度:快速幂\(O(logn)\),根据矩阵大小不同会带不同大小的常数
大模拟
猪国杀为例
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#define loves =
using std::cin;
using std::cout;
using std::vector;
using std::queue;
using std::string;
int n, m; // n -> 玩家数目 m -> 牌堆牌的数量
int sf[12]; // 身份
int lsf[12]; // 身份确定前的类身份
int health[12]; // 血量
bool crossbow[12]; // 猪哥连弩
bool died[12]; // 是否已死
vector<char>shoupai[12]; // 每只猪手牌
queue<char>heap; //牌堆
// ============card部分============
char get_card() { // 牌堆里抽卡
char ch = heap.front();
heap.pop();
if (heap.empty()) heap.push(ch);
return ch;
}
void Erase_card(int rt, char ch) {
for (auto it = shoupai[rt].begin(); it != shoupai[rt].end(); it++)
if (*it == ch) {
shoupai[rt].erase(it);
return ;
}
}
bool has_card(int rt, char ch) {
for (auto it : shoupai[rt])
if (it == ch) return true;
return false;
}
// ============card部分============
// ============读入部分============
void input() {
cin >> n >> m;
for (int i =1 ; i <= n; i++) {
string temp;
cin >> temp; health[i] = 4;
if (temp[0] == 'M' || temp[0] == 'Z') sf[i] = 1;
else sf[i] = 2;
for (int j = 1; j <= 4; j++) {
char ch;
cin >> ch;
shoupai[i].push_back(ch);
}
}
while (m--) {
char ch;
cin >> ch;
heap.push(ch);
}
}
// ============读入部分============
// ============输出部分============
void output() {
int num_good = 0, num_bad = 0;
for (int i = 1; i <= n; i++)
if (!died[i])
if (sf[i] == 1)
num_good++;
else num_bad++;
if (num_good && !died[1]) puts("MP");
else puts("FP");
for (int i = 1; i <= n; i++) {
if (died[i]) puts("DEAD");
else {
for (auto it : shoupai[i]) cout << it << ' ';
puts("");
}
}
exit(0);
}
// ============输出部分============
// ============无懈可击============
bool can_WX(int fr, int rt, bool isWX) {
// fr used WX for rt
int nxt = fr;
do {
if (!died[nxt] && (isWX ? sf[nxt] != lsf[rt] : sf[nxt] == lsf[rt]) && has_card(nxt, 'J')) {
Erase_card(nxt, 'J');
lsf[nxt] = sf[nxt];
if (can_WX(nxt, nxt, true))
return false;
else return true;
}
++nxt;
if (nxt == n + 1) nxt = 1;
} while (nxt != fr);
return false;
}
// ============无懈可击============
// ============掉血============
void be_heart(int fr, int rt, bool jinnang) {
health[rt]--;
//std::cout << rt << "heart by" << fr << '\n';
if (rt == 1 && lsf[fr] == 0)
lsf[fr] = 3;
if (!jinnang && lsf[fr] && lsf[fr] != 3)
lsf[fr] = sf[fr];
if (health[rt]) return ;
if (!health[rt] && has_card(rt, 'P')) {
Erase_card(rt, 'P');
++health[rt];
}
else {
died[rt] = true;
if (rt == 1) output();
int num_bad = 0;
for (int i =1 ; i <= n; i++)
if (!died[i])
if (sf[i] == 2) num_bad++;
if (num_bad == 0) output();
else {
if (fr == 1 && sf[rt] == 1) {
shoupai[1].clear();
crossbow[1] = false;
}
if (sf[rt] == 2) {
shoupai[fr].push_back(get_card());
shoupai[fr].push_back(get_card());
shoupai[fr].push_back(get_card());
}
}
}
}
// ============掉血============
// ============南猪入侵============
void use_NM(int rt) {
int nxt = rt + 1;
if (nxt == n + 1) nxt = 1;
while (nxt != rt) {
if (!died[nxt]) {
if (can_WX(rt, nxt, false)) {
++nxt;
if (nxt == n + 1) nxt = 1;
continue;
}
if (has_card(nxt, 'K'))
Erase_card(nxt, 'K');
else {
be_heart(rt, nxt, true);
//std::cout << rt << "uesd NM hit" << nxt << "heart 1 滴血" << '\n';
}
}
++nxt;
if (nxt == n + 1) nxt = 1;
}
}
// ============南猪入侵============
// ============万箭齐发============
void use_WJ(int rt) {
int nxt = rt + 1;
if (nxt == n + 1) nxt = 1;
while (nxt != rt) {
if (!died[nxt]) {
if (can_WX(rt, nxt, false)) {
++nxt;
if (nxt == n + 1) nxt = 1;
continue;
}
if (has_card(nxt, 'D'))
Erase_card(nxt, 'D');
else {
be_heart(rt, nxt, true);
//std::cout << rt << "uesd WJ hit" << nxt << "heart 1 滴血" << '\n';
}
}
++nxt;
if (nxt == n + 1) nxt = 1;
}
}
// ============万箭齐发============
// ============决斗============
void duel(int fr, int to) {
if (!has_card(to, 'K') || (fr == 1 && sf[to] == 1)) {be_heart(fr, to, true); return ;}
else {
Erase_card(to, 'K');
duel(to, fr);
}
}
// ============决斗============
// ============寻找下一个杀或决斗的目标============
int Find(int rt, bool limit) {
if (!limit && sf[rt] == 2) return 1;
int nxt = rt + 1;
if (nxt == n + 1) nxt = 1;
while (nxt != rt) {
//std::cout << "now find mubiao:" << nxt << '\n';
if (died[nxt]) {
++nxt;
if (nxt == n + 1) nxt = 1;
continue;
}
bool canhit = (rt == 1 ? lsf[nxt] >= 2 : lsf[nxt] != sf[rt]);
if (rt > 1) {
if (lsf[nxt] == 0 || lsf[nxt] == 3) canhit = false;
else canhit = (lsf[nxt] != sf[rt]);
}
if (canhit) return nxt;
else if (limit) return 0;
++nxt;
if (nxt == n + 1) nxt = 1;
}
return 0;
}
// ============寻找下一个杀或决斗的目标============
// ============游戏过程============
void solve() {
lsf[1] = 1;
int rt = 0;
while (true) {
++rt;
if (rt == n + 1) rt = 1;
if (died[rt]) continue;
shoupai[rt].push_back(get_card());
shoupai[rt].push_back(get_card());
bool usedk = false;
while (true) {
if (died[rt]) break;
bool usedcard = false;
for (auto i = shoupai[rt].begin(); i != shoupai[rt].end(); i++) {
char it = *i;
if (it == 'P') {
if (health[rt] != 4) {
++health[rt];
Erase_card(rt, 'P');
usedcard = true;
break;
}
}
else if (it == 'N'){
Erase_card(rt, 'N');
use_NM(rt);
usedcard = true;
break;
}
else if (it == 'W'){
Erase_card(rt, 'W');
use_WJ(rt);
usedcard = true;
break;
}
else if (it == 'Z') {
crossbow[rt] = true;
Erase_card(rt, 'Z');
usedcard = true;
break;
}
else if (it == 'K') {
if (usedk && !crossbow[rt]) continue;
int to = Find(rt, true);
if (!to) continue;
Erase_card(rt, 'K');
usedcard = usedk = true;
if (has_card(to, 'D')) {Erase_card(to, 'D'); ++health[to];}
be_heart(rt, to, false);
break;
}
else if (it == 'F') {
int to = Find(rt, false);
if (!to) continue;
lsf[rt] = sf[rt];
Erase_card(rt, 'F');
usedcard = true;
if (can_WX(rt, to, false)) break;
duel(rt, to);
usedcard = true;
break;
}
}
if (usedcard) continue;
break;
}
}
}
// ============游戏过程============
int haj() {
input();
solve();
return 0;
}
int ziiidan loves haj();
int main() {}
大概……也许……就这些了吧……
\(CSP-S\ 2019\) \(RP++!!!\)