板子
upd at 2025.02.16
其实早就断更了。如果想了解更多详情,请前往 Zctf1088 的博客【学习笔记/算法总结】
upd at 2025.02.09
鉴于前段时间校 OJ 上的博客爆炸。为避免重大损失,搬迁至此。
板子
是在我学斜率优化dp的时候,觉得需要一些模板。纯粹就是按写入的顺序排的(由新到旧),一些简单的就没写。也没啥可说的,拿来给我自己用的,如果能帮助到别人就更好。不断在更新,一有比较复杂且框架清晰的算法,就会写上去。另外,做题过程中也会对此进行补充。如发现有问题或错误,请及时告知,谢谢。
数学
--高精度
const int maxn = 1005;
struct Int {
int a[2 * maxn];
Int() {
memset(a, 0, sizeof(a));
}
Int(int x) {
*this = x;
}
Int operator = (int & x) {
for (int i = 0; x; i++, x /= 10) {
a[i] = x % 10;
}
return *this;
}
bool operator == (const Int & b) const {
for (int i = maxn - 1; i >= 0; i--) {
if (a[i] ^ b.a[i]) return false;
}
return true;
}
bool operator > (const Int & b) const {
for (int i = maxn - 1; i >= 0; i--) {
if (a[i] ^ b.a[i]) return a[i] > b.a[i];
}
return false;
}
bool operator < (const Int & b) const {
for (int i = maxn - 1; i >= 0; i--) {
if (a[i] ^ b.a[i]) return a[i] < b.a[i];
}
return false;
}
Int operator + (const Int & b) const {
Int c;
for (int i = 0; i < 2 * maxn - 1; i++) {
c.a[i] += a[i] + b.a[i];
while (c.a[i] > 9) {
c.a[i + 1] += 1;
c.a[i] -= 10;
}
}
return c;
}
Int operator - (const Int & b) const {
Int c;
for (int i = 0; i < 2 * maxn - 1; i++) {
c.a[i] += a[i] - b.a[i];
if (c.a[i] < 0) {
c.a[i + 1] -= 1;
c.a[i] += 10;
}
}
return c;
}
Int operator * (const Int & b) const {
Int c;
for (int i = 0; i < maxn; i++) {
for (int j = 0; j < maxn; j++) {
c.a[i + j] += a[i] * b.a[j];
}
}
for (int i = 0; i < 2 * maxn - 1; i++) {
while (c.a[i] > 9) {
c.a[i + 1] += 1;
c.a[i] -= 10;
}
}
return c;
}
bool operator != (const Int & b) { return !(*this == b); }
bool operator <= (const Int & b) { return !(*this > b); }
bool operator >= (const Int & b) { return !(*this < b); }
Int operator += (const Int & b) { return *this = *this + b; }
Int operator -= (const Int & b) { return *this = *this - b; }
Int operator *= (const Int & b) { return *this = *this * b; }
void read() {
string s;
cin >> s;
int ll = s.size();
for (int i = 0, j = ll - 1; j >= 0; i++, j--) {
a[i] = s[j] - '0';
}
}
void print() {
int flag = 0;
for (int i = 2 * maxn - 1; i >= 0; i--) {
if (a[i]) flag = 1;
if (flag || !i) putchar(a[i] + '0');
}
}
};
--快速幂
int fpow(int a, int x, int MOD) {
a %= MOD;
int ans = 1;
while (x > 0) {
if (x & 1) {
ans = ans * a % MOD;
}
a = a * a % MOD;
x >>= 1;
}
return ans;
}
--欧几里得辗转相除法 最大公约数
int gcdd(int n, int m) {
if (m == 0) return n;
return gcdd(m, n % m);
}
--扩展欧几里得
求 \(ax+by=\gcd(a,b)\) 的一组正整数解(\(x\)、\(y\)为未知数)
构造通解
\(x=x_0+\frac{b}{\gcd(a,b)}*k\)
\(y=y_0-\frac{a}{\gcd(a,b)}*k\)
int exgcd(int a, int b, int &x, int &y) {
if (b == 0) {
x = 1;
y = 0;
return a;
}
int ret = exgcd(b, a % b, x, y);
int t = x;
x = y;
y = t - a / b * y;
return ret;
}
求不定方程 \(ax+by=c\) 的一组整数解
- 若 \(\gcd(a,b) \mid c\),则有整数解
先用扩欧求 \(ax+by=\gcd(a,b)\) 的解
再乘以 \(\frac{c}{\gcd(a,b)}\),即得原方程特解\((x_0,y_0)\)。 - 若 \(\gcd(a,b) \nmid c\),则无整数解。
构造通解同上。
--乘法逆元
费马小定理求逆元(快速幂)
int ny(int x) {
return fpow(x, MOD - 2);
}
扩展欧几里得求逆元
int ny(int num, int MOD) {
int x, y;
int g = exgcd(num, MOD, x, y);
if (g == 1) {
return (x + MOD) % MOD;
} else {
return -1;
}
}
线性求逆元
ny[1] = 1;
for (int i = 2; i <= n; i++) {
ny[i] = (1ll * p - p / i) * ny[p % i] % p;
}
--欧拉函数
int phi(int x) {
int ans = x, n = x;
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) {
ans = ans * (1.0 - 1.0 / i);
while (n % i == 0) n /= i;
}
}
if (n > 1) ans = ans * (1.0 - 1.0 / n);
return ans;
}
void init() {
f[1] = 1;
for (int i = 2; i < N; i++) {
if (f[i] == 0) {
p[++tot] = i;
phi[i] = i - 1;
}
for (int j = 1; j <= tot; j++) {
if (i * p[j] >= N) break;
f[i * p[j]] = 1;
if (i % p[j] == 0) {
phi[i * p[j]] = p[j] * phi[i];
} else {
phi[i * p[j]] = phi[p[j]] * phi[i];
}
}
}
}
--欧拉定理
若 \(\gcd(a,m)=1\),则
--扩展欧拉定理
--组合数
int C(int n, int m) {
if (n < m) {
return 0;
}
return frac[n] * ny[m] % MOD * ny[n - m] % MOD;
}
frac[0] = ny[0] = 1; // 事实上阶乘应该是fact,但这个写习惯了
for (int i = 1; i <= n; i++) {
frac[i] = frac[i - 1] * i % MOD;
ny[i] = ny[i - 1] * fpow(i, MOD - 2) % MOD;
}
for (int i = 1; i <= n; i++) {
C[i][0] = 1;
for (int j = 1; j <= i; j++) {
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % MOD;
}
}
--lucas定理
可以用于求模数较小时且模数为质数的 \(C_n^m\)
int C(int n, int m) {
if (n < m) return 0;
return frac[n] * ny[m] % MOD * ny[n - m] % MOD;
}
int lucas(int n, int m) {
if (m == 0) return 1;
return lucas(n / MOD, m / MOD) * C(n % MOD, m % MOD) % MOD;
}
void init() {
frac[0] = ny[0] = 1;
for (int i = 1; i <= MOD; i++) {
frac[i] = frac[i - 1] * i % MOD;
ny[i] = ny[i - 1] * fpow(i, MOD - 2) % MOD;
}
}
--矩阵乘法
struct mar {
int m[M][M];
mar() {
for (int i = 0; i < M; i++)
for (int j = 0; j < M; j++) m[i][j] = 0;
}
il void init() {
for (int i = 0; i < M; i++) m[i][i] = 1;
}
il int * operator [] (int x) {
return m[x];
}
il mar operator * (mar x) const {
mar res;
for (int i = 0; i < M; i++)
for (int j = 0; j < M; j++)
for (int k = 0; k < M; k++)
add(res[i][j], m[i][k] * x[k][j] % MOD);
return res;
}
};
--高斯消元
void gauss() {
for (int i = 1; i <= n; i++) {
int r = i;
for (int k = i; k <= n; k++) {
if (fabs(a[k][i]) > eps) {
r = k;
break;
}
}
if (r != i) {
swap(a[r], a[i]);
}
if (fabs(a[i][i]) < eps) {
return;
}
for (int j = n + 1; j >= i; j--) {
a[i][j] /= a[i][i];
}
for (int k = i + 1; k <= n; k++) {
for (int j = n + 1; j >= i; j--) {
a[k][j] -= a[k][i] * a[i][j];
}
}
}
for (int i = n - 1; i >= 1; i--) {
for (int j = i + 1; j <= n; j++) {
a[i][n + 1] -= a[i][j] * a[j][n + 1];
}
}
return;
}
int gauss() {
int c = 1;
for (int i = 1; i <= n; i++) {
int r = c;
for (int k = c; k <= n; k++) {
if (fabs(a[k][i]) > fabs(a[r][i])) {
r = k;
}
}
if (fabs(a[r][i]) < eps) {
continue;
}
swap(a[r], a[c]);
for (int k = n + 1; k >= i; k--) {
a[c][k] /= a[c][i];
}
for (int k = c + 1; k <= n; k++) {
if (fabs(a[k][i]) > eps) {
for (int j = n + 1; j >= i; j--) {
a[k][j] -= a[c][j] * a[k][i];
}
}
}
c++;
}
if (c <= n) {
for (int i = c; i <= n; i++) {
if (fabs(a[i][n + 1]) > eps) {
return -1;
}
}
return 0;
}
for (int i = n; i >= 1; i--) {
for (int j = i + 1; j <= n; j++) {
a[i][n + 1] -= a[j][n + 1] * a[i][j];
}
}
return 1;
}
--二项式反演
--中国剩余定理 CRT
求解线性同余方程组
(其中模数 \(m_1,m_2,\dots,m_n\) 为两两互质整数)
求 \(x\) 的最小非负整数解。
int CRT(int m[], int r[]) {
__int128 M = 1, ans = 0; // 数据太大了就用 __int128
for (int i = 1; i <= n; i++) M *= m[i];
for (int i = 1; i <= n; i++) {
__int128 c = M / m[i];
ans = (ans + r[i] * c * ny(c, m[i]) % M) % M; // 用扩欧求逆元,因为模数不一定为质数
}
return ans;
}
--扩展中国剩余定理
求解线性同余方程组
(其中模数 \(m_1,m_2,\dots,m_n\) 为不一定两两互质整数)
求 \(x\) 的最小非负整数解。(两两互质,不代表两个数都是质数)
前两个方程: \(x \equiv r_{1} \pmod {m_{1}}, x \equiv r_{2} \pmod {m_{2}}\)
转化为不定方程: \(x=m_{1} p+r_{1}=m_{2} q+r_{2}\)
则 \(m_{1} p-m_{2} q=r_{2}-r_{1}\)
由裴蜀定理,
当 \(\gcd\left(m_{1}, m_{2}\right) \nmid\left(r_{2}-r_{1}\right)\) 时,无解
当 \(\gcd\left(m_{1}, m_{2}\right) \mid\left(r_{2}-r_{1}\right)\) 时,有解
由扩欧算法,
得特解 \(p=p * \frac{r_{2}-r_{1}}{g c d}, q=q * \frac{r_{2}-r_{1}}{g c d}\)
所以 \(x=m_{1} p+r_{1}=\frac{m_{1} m_{2}}{g c d} * k+m_{1} p+r_{1}\)
前两个方程等价合并为一个方程 \(x \equiv r \pmod m\)
其中 \(r=m_{1} p+r_{1}\) , \(m=\operatorname{lcm}\left(m_{1}, m_{2}\right)\)
所以 \(n\) 个同余方程只要合并 \(n-1\) 次,即可求解
int EXCRT(int m[], int r[]) {
__int128 m1, m2, r1, r2, p, q;
m1 = m[1], r1 = r[1];
for (int i = 2; i <= n; i++) {
m2 = m[i], r2 = r[i];
__int128 a = m1, b = m2, c = r2 - r1; // 扩展欧几里得
__int128 g = exgcd(a, b, p, q);
if (c % g != 0) return -1; // 无解
p = p * c / g; // 特解
p = (p % (b / g) + (b / g)) % (b / g);
r1 = m1 * p + r1;
m1 = m1 * m2 / g;
}
return (r1 % m1 + m1) % m1;
}
--卡特兰数
卡特兰数的常见公式:
--pollard
int pollard(int n, int c) {
int x, y, d, i = 1, k = 2;
x = wdz() * wdz() % (n - 1) + 1;
y = x;
while (1) {
x = (mul(x, x, n) + c) % n;
d = gcd((x - y + n) % n, n);
if (1 < d && d < n) return d;
if (x == y) return n;
if (++i == k) {
k <<= 1;
y = x;
}
}
return 23333333;
}
--Pollard Rho和Miller Rabin
int Test[15] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};
int test_time = 12;
bool millerRabin(long long p) {
if (p < 2) return 0;
long long t = p - 1, k = 0;
while (!(t & 1)) {
t >>= 1;
k++;
}
for (long long i = 0; i < test_time; i++) {
if (p == Test[i]) return 1;
long long a = fpow(Test[i], t, p);
for (long long j = 1; j <= k; j++) {
if ((__int128)a * a % p == 1 && a != 1 && a != p - 1) {
return 0;
}
a = (__int128)a * a % p;
}
if (a != 1) return 0;
}
return 1;
}
long long pollard(long long n, long long c) {
long long x, y, d, i = 1, k = 2, v = 1;
x = y = 0;
while (1) {
x = ((__int128)x * x % n + c) % n;
v = (__int128)v * abs(x - y) % n;
if (i % 127 == 0) {
d = gcd(v, n);
if (1 < d) return d;
}
if (x == y) return n;
if (++i == k) {
k <<= 1;
y = x;
d = gcd(v, n);
if (1 < d) return d;
}
}
return 23333333;
}
long long n, ans;
void resolve(long long x) {
if (x == 1 || x <= ans) return;
if (millerRabin(x)) {
ans = max(ans, x);
return;
}
long long p = x;
while (p >= x) {
p = pollard(x, wdz() % (x - 1) + 1);
}
while (x % p == 0) x /= p;
resolve(x);
resolve(p);
}
动态规划
--斜率优化dp
对于
int up(int j, int k) {
return dp[j] - dp[k];
}
int down(int j, int k) {
return a[j] - a[k];
}
int main() {
memset(dp, INF, sizeof(dp));
dp[0] = 0;
int head = 1, tail = 0;
q[++tail] = 0;
for (int i = 1; i <= n; i++) {
while (head < tail && up(q[head + 1], q[head]) <= b[i] * down(q[head + 1], q[head])) {
head++;
}
dp[i] = dp[q[head]]/*略*/;
while (head < tail && up(q[tail], q[tail - 1]) * down(i, q[tail]) >= up(i, q[tail]) * down(q[tail], q[tail - 1])) {
tail--;
}
q[++tail] = i;
}
printf("%lld\n", dp[n]);
return 0;
}
对于
将只与 \(j\) 有关的项放在左边,只与 \(i\) 有关的项放在右边,同时带有 \(i\) 和 \(j\) 的项中以与 \(i\) 有关的参数作为斜率。且斜率不能为负。
int dp[N], q[N], head = 1, tail = 0;
int y(int j)
int k(int i)
int x(int j)
int b(int i)
double slope(int i, int j) {
return 1.0 * (y(i) - y(j)) / (x(i) - x(j));
}
int main() {
memset(dp, INF, sizeof(dp));
dp[0] = 0;
q[++tail] = 0;
for (int i = 1; i <= n; i++) {
while (head < tail && slope(q[head], q[head + 1]) < k(i)) {
head++;
}
// y = kx + b,转移 dp[i],略
while (head < tail && slope(q[tail - 1], q[tail]) >= slope(q[tail], i)) {
tail--;
}
q[++tail] = i;
}
printf("%lld\n", dp[n]);
return 0;
}
--状压dp
题目详情 - 「SCOI2005」互不侵犯 - gxyz (gxyzoj.com)
#include <bits/stdc++.h>
using namespace std;
const int N = 9 + 5;
const int M = (1 << 9) + 5;
const int K = N * N;
int n, k;
long long dp[N][K][M];
long long stt[M], cnt[M];
int lowbit(int x) {
return x & -x;
}
int main() {
scanf("%d%d", &n, &k);
int ms = (1 << n) - 1;
int tot = 0;
for (int s = 0; s <= ms; s++) {
for (int tmp = s; tmp > 0; tmp -= lowbit(tmp)) {
cnt[s]++;
}
if (((s << 1) & s) == 0 && ((s >> 1) & s) == 0 && cnt[s] <= k) {
stt[++tot] = s;
}
}
dp[0][0][0] = 1;
for (int i = 1; i <= n; i++) {
for (int t1 = 1; t1 <= tot; t1++) {
int s = stt[t1];
for (int t2 = 1; t2 <= tot; t2++) {
int ss = stt[t2];
if ((ss & s) == 0 && ((ss << 1) & s) == 0 && ((ss >> 1) & s) == 0) {
for (int j = 0; j <= k; j++) {
if (cnt[s] <= j) {
dp[i][j][s] += dp[i - 1][j - cnt[s]][ss];
}
}
}
}
}
}
long long ans = 0;
for (int i = 1; i <= tot; i++) {
ans += dp[n][k][stt[i]];
}
printf("%lld\n", ans);
return 0;
}
--单调队列优化dp
题目详情 - 「一本通 5.5 练习 1」烽火传递 - gxyz (gxyzoj.com)
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n, m;
int a[N];
deque<int> q;
int dp[N];
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
q.push_back(0);
for (int i = 1; i <= n; i++) {
if (q.front() < i - m) {
q.pop_front();
}
dp[i] = dp[q.front()] + a[i];
while (!q.empty() && dp[i] < dp[q.back()]) {
q.pop_back();
}
q.push_back(i);
}
int ans = 0x3f3f3f3f;
for (int i = n - m + 1; i <= n; i++) {
ans = min(ans, dp[i]);
}
printf("%d\n", ans);
return 0;
}
单调队列优化多重背包
#include <bits/stdc++.h>
using namespace std;
const int N = 5e2 + 10, M = 6e3 + 10;
int v[N], w[N], s[N];
int dp[N][M];
deque<int> q;
int main() {
int m, n;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%d%d%d", &v[i], &w[i], &s[i]); // 面值 体积 数量
}
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= m; j++) {
dp[i][j] = dp[i-1][j];
}
for (int r = 0; r < v[i]; r++) {
while (!q.empty()) {
q.pop_back();
}
for (int k = 0; k * v[i] + r <= m; k++) {
if (!q.empty() && q.front() < k - s[i]) {
q.pop_front();
}
while (!q.empty() && dp[i-1][k * v[i] + r] - k * w[i] >= dp[i-1][q.back() * v[i] + r] - q.back() * w[i]) {
q.pop_back();
}
q.push_back(k);
dp[i][k * v[i] + r] = dp[i-1][q.front() * v[i] + r] - q.front() * w[i] + k * w[i];
}
}
}
printf("%d", dp[n][m]);
return 0;
}
字符串
--哈希
void init() {
p[0] = 1;
for (int i = 1; i <= 10000; i++) {
p[i] = p[i-1] * P;
}
int len = strlen(s + 1);
for (int i = 1; i <= len; i++) {
hash[i] = hash[i-1] * P + s[i];
}
}
unsigned long long gethash(int l, int r) {
return hash[r] - hash[l - 1] * p[r - l + 1];
}
--KMP
void getnxt() {
for (int i = 2, j = 0; i <= m; i++) {
while (j && t[i] != t[j + 1]) {
j = nxt[j];
}
if (t[i] == t[j + 1]) {
j++;
}
nxt[i] = j;
}
}
void kmp() {
for (int i = 1, j = 0; i <= n; i++) {
while (j && s[i] != t[j + 1]) {
j = nxt[j];
}
if (s[i] == t[j + 1]) {
j++;
}
if (j >= m) {
printf("%d\n", i - j + 1);
}
}
}
--trie树
void ist(string s) {
int p = 0;
for (int i = 0; i < s.size(); i++) {
if (!trie[p][s[i]]) trie[p][s[i]] = ++tot;
p = trie[p][s[i]];
}
cnt[p]++;
}
int fid(string s) {
int p = 0;
for (int i = 0; i < s.size(); i++) {
if (!trie[p][s[i]]) return 0;
p = trie[p][s[i]];
}
return cnt[p];
}
--AC自动机
// AC自动机的dp都非常套路
//dp[i][j]表示当前在节点i,且串长为j时的情况
//有时再加一维表示这个状态里包含了哪些东西
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n;
char s[N];
int trie[N][26], cnt[N], flag[N], fail[N];
int tot;
queue<int> q;
void insert(char s[]) {
int u = 1, len = strlen(s);
for (int i = 0; i < len; i++) {
int v = s[i] - 'a';
if (!trie[u][v]) trie[u][v] = ++tot;
u = trie[u][v];
}
cnt[u]++;
}
void getfail() {
for (int i = 0; i < 26; i++) trie[0][i] = 1;
q.push(1);
fail[1] = 0;
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = 0; i < 26; i++) {
if (!trie[u][i]) {
trie[u][i] = trie[fail[u]][i];
continue;
}
fail[trie[u][i]] = trie[fail[u]][i];
q.push(trie[u][i]);
}
}
}
int query(char s[]) {
int u = 1, len = strlen(s), ans = 0;
for (int i = 0; i < len; i++) {
int v = s[i] - 'a';
int k = trie[u][v];
while (k > 1 && !flag[k]) {
ans = ans + cnt[k];
flag[k] = 1;
k = fail[k];
}
u = trie[u][v];
}
return ans;
}
int main() {
tot = 1;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%s", s);
insert(s);
}
getfail();
scanf("%s", s);
printf("%d\n", query(s));
return 0;
}
数据结构
--哈希表
struct hashtable {
struct data {
int u, v, nxt;
};
data e[N];
int h[N], cnt;
int hash(int u) {
return (u % N + N) % N;
}
int query(int u) {
int hu = hash(u);
for (int i = h[hu]; i; i = e[i].nxt) {
if (e[i].u == u) {
return e[i].v;
}
}
return 0;
}
void update(int u, int v) {
int hu = hash(u);
for (int i = h[hu]; i; i = e[i].nxt) {
if (e[i].u == u) {
e[i].v = v;
return;
}
}
e[++cnt] = {u, v, h[hu]};
h[hu] = cnt;
}
};
封装:
struct hash_map {
struct data {
int u, v, nxt;
};
data e[N << 1];
int h[N], cnt;
int hash(int u) {
return (u % N + N) % N;
}
int& operator [] (int u) {
int hu = hash(u);
for (int i = h[hu]; i != 0; i = e[i].nxt) {
if (e[i].u == u) {
return e[i].v;
}
}
e[++cnt] = {u, 0, h[hu]};
h[hu] = cnt;
return e[cnt].v;
}
hash_map() {
cnt = 0;
memset(h, 0, sizeof(h));
}
};
--并查集
int fid(int x) {
if (x != fa[x]) {
fa[x] = fid(fa[x]);
}
return fa[x];
}
--可撤销并查集
// siz表示联通块内的点的数量,cnt是联通块内边的数量
int getfa(int x) {
if (x != fa[x]) {
return getfa(fa[x]);
}
return fa[x];
}
void link(int &ans, int x, int y) {
int xf = getfa(x); xx.push(xf);
int yf = getfa(y); yy.push(yf);
if (xf == yf) {
cnt[xf]++;
} else {
if (siz[xf] < siz[yf]) swap(xf, yf);
siz[xf] += siz[yf];
cnt[xf] += cnt[yf];
cnt[xf]++;
fa[yf] = xf;
}
}
void cut(int &ans) {
int tx = xx.top(); xx.pop();
int ty = yy.top(); yy.pop();
if (tx == ty) {
cnt[tx]--;
} else {
siz[tx] -= siz[ty];
cnt[tx] -= cnt[ty];
cnt[tx]--;
fa[ty] = ty;
}
}
--单调队列
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int a[N];
deque<int> q;
int main() {
int n, k;
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
// min
for (int i = 1; i <= n; i++) {
while (!q.empty() && a[i] < a[q.back()]) {
q.pop_back();
}
q.push_back(i);
while (!q.empty() && q.front() < i - k + 1) {
q.pop_front();
}
if (i >= k) {
printf("%d ", a[q.front()]);
}
}
printf("\n");
while (!q.empty()) q.pop_back();
// max
for (int i = 1; i <= n; i++) {
while (!q.empty() && a[i] > a[q.back()]) {
q.pop_back();
}
q.push_back(i);
while (!q.empty() && q.front() < i - k + 1) {
q.pop_front();
}
if (i >= k) {
printf("%d ", a[q.front()]);
}
}
return 0;
}
--树状数组
// 极其粗糙地来凑个数
struct BIT {
int c[N];
int lowbit(int x) {
return x & -x;
}
void update(int x, int v) {
for (int i = x; i <= M; i += lowbit(i)) {
c[i] += v;
}
}
int query(int x) {
int ret = 0;
for (int i = x; i > 0; i -= lowbit(i)) {
ret += c[i];
}
return ret;
}
int query(int l, int r) {
return query(r) - query(l - 1);
}
};
--线段树
----单点修改,区间查询
struct Tree {
int l, r;
int s;
};
Tree tree[4 * N];
void build(int p, int l, int r) {
tree[p].l = l;
tree[p].r = r;
if (l == r) {
tree[p].s = a[l];
return;
}
int mid = (l + r) / 2;
build(p*2, l, mid);
build(p*2+1, mid+1, r);
tree[p].s = tree[p*2].s + tree[p*2+1].s;
}
void add(int p, int x, int v) {
if (tree[p].l == tree[p].r) {
tree[p].s += v;
return;
}
int mid = (tree[p].l + tree[p].r) / 2;
if (x <= mid) {
add(p * 2, x, v);
} else {
add(p * 2 + 1, x, v);
}
tree[p].s = tree[p * 2].s + tree[p * 2 + 1].s;
}
int sum(int p, int l, int r) {
if (l <= tree[p].l && tree[p].r <= r) {
return tree[p].s;
}
int mid = (tree[p].l + tree[p].r) / 2;
int ret = 0;
if (l <= mid) {
ret += sum(p * 2, l, r);
}
if (r > mid) {
ret += sum(p * 2 + 1, l, r);
}
return ret;
}
----区间修改,单点查询
struct Tree {
int l, r;
long long s;
long long lazy;
};
Tree tree[4 * N];
void build(int p, int l, int r) {
tree[p].l = l;
tree[p].r = r;
if (l == r) {
tree[p].s = a[l];
return;
}
int mid = (l + r) / 2;
build(p*2, l, mid);
build(p*2+1, mid+1, r);
tree[p].s = tree[p*2].s + tree[p*2+1].s;
}
void pushdown(int p) {
int lc = p * 2;
int rc = p * 2 + 1;
if (tree[p].lazy && tree[p].l != tree[p].r) {
tree[lc].lazy += tree[p].lazy;
tree[rc].lazy += tree[p].lazy;
tree[lc].s += tree[p].lazy * (tree[lc].r - tree[lc].l + 1);
tree[rc].s += tree[p].lazy * (tree[rc].r - tree[rc].l + 1);
tree[p].lazy = 0;
}
}
void add(int p, int l, int r, long long v) {
pushdown(p);
if (tree[p].l == tree[p].r) {
tree[p].s += v;
return;
}
int lc = p * 2;
int rc = p * 2 + 1;
int mid = (tree[p].l + tree[p].r) / 2;
if (r <= mid) {
add(lc, l, r, v);
} else if (l > mid) {
add(rc, l, r, v);
} else {
add(lc, l, r, v);
add(rc, l, r, v);
}
tree[p].s = tree[lc].s + tree[rc].s;
}
long long sum(int p, int x) {
pushdown(p);
if (tree[p].l == tree[p].r) {
return tree[p].s;
}
int lc = p * 2;
int rc = p * 2 + 1;
int mid = (tree[p].l + tree[p].r) / 2;
if (x <= mid) {
return sum(lc, x);
} else {
return sum(rc, x);
}
}
----区间修改,区间查询
long long n, m, a[N];
struct Tree {
long long l, r, s, lazy;
};
Tree tree[4 * N];
void build(int p, int l, int r) {
tree[p].l = l;
tree[p].r = r;
if (l == r) {
tree[p].s = a[l];
return;
}
int mid = (l + r) / 2;
int lc = 2 * p;
int rc = 2 * p + 1;
build(lc, l, mid);
build(rc, mid + 1, r);
tree[p].s = tree[lc].s + tree[rc].s;
}
void pushdown(int p) {
int lc = 2 * p;
int rc = 2 * p + 1;
if (tree[p].lazy && tree[p].l != tree[p].r) {
tree[lc].lazy += tree[p].lazy;
tree[rc].lazy += tree[p].lazy;
tree[lc].s += tree[p].lazy * (tree[lc].r - tree[lc].l + 1);
tree[rc].s += tree[p].lazy * (tree[rc].r - tree[rc].l + 1);
tree[p].lazy = 0;
}
}
void update(int p, int l, int r, long long v) {
pushdown(p);
if (l == tree[p].l && tree[p].r == r) {
tree[p].lazy += v;
tree[p].s += v * (tree[p].r - tree[p].l + 1);
return;
}
int mid = (tree[p].l + tree[p].r) / 2;
int lc = 2 * p;
int rc = 2 * p + 1;
if (mid < l) {
update(rc, l, r, v);
} else if (r <= mid) {
update(lc, l, r, v);
} else {
update(lc, l, mid, v);
update(rc, mid + 1, r, v);
}
tree[p].s = tree[lc].s + tree[rc].s;
}
long long query(int p, int l, int r) {
pushdown(p);
if (l <= tree[p].l && tree[p].r <= r) {
return tree[p].s;
}
int mid = (tree[p].l + tree[p].r) / 2;
int lc = 2 * p;
int rc = 2 * p + 1;
if (mid < l) {
return query(rc, l, r);
} else if (r <= mid) {
return query(lc, l, r);
} else {
return query(lc, l, mid) + query(rc, mid + 1, r);
}
}
----区间最值
struct node {
int l, r, mn, lazy;
};
node tree[4 * N];
void pushdown(int p) {
if (tree[p].lazy && tree[p].l != tree[p].r) {
int lc = p * 2, rc = p * 2 + 1;
tree[lc].lazy += tree[p].lazy;
tree[rc].lazy += tree[p].lazy;
tree[lc].mn += tree[p].lazy;
tree[rc].mn += tree[p].lazy;
tree[p].lazy = 0;
}
}
void build(int p, int l, int r) {
tree[p].l = l, tree[p].r = r;
tree[p].lazy = 0;
if (l == r) {
tree[p].mn = dp[l];
return;
}
int mid = (l + r) / 2;
int lc = p * 2, rc = p * 2 + 1;
build(lc, l, mid);
build(rc, mid + 1, r);
tree[p].mn = min(tree[lc].mn, tree[rc].mn);
}
void update(int p, int l, int r, int v) {
if (l > r) return;
pushdown(p);
if (l == tree[p].l && tree[p].r == r) {
tree[p].lazy += v;
tree[p].mn += v;
return;
}
int mid = (tree[p].l + tree[p].r) / 2;
int lc = 2 * p, rc = 2 * p + 1;
if (r <= mid) {
update(lc, l, r, v);
} else if (l > mid) {
update(rc, l, r, v);
} else {
update(lc, l, mid, v);
update(rc, mid + 1, r, v);
}
tree[p].mn = min(tree[lc].mn, tree[rc].mn);
}
int query(int p, int l, int r) {
if (l > r) return INF;
pushdown(p);
if (l == tree[p].l && tree[p].r == r) {
return tree[p].mn;
}
int mid = (tree[p].l + tree[p].r) / 2;
int lc = 2 * p, rc = 2 * p + 1;
if (r <= mid) {
return query(lc, l, r);
} else if (l > mid) {
return query(rc, l, r);
} else {
return min(query(lc, l, mid), query(rc, mid + 1, r));
}
}
----区间最大公约数
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
long long n, m, a[N];
struct Tree {
long long l, r, s, g;
};
Tree tree[4 * N];
long long gcdd(long long n, long long m) {
if (n == 0) {
return m;
}
return gcdd(m % n, n);
}
void build(int p, int l, int r) {
tree[p].l = l;
tree[p].r = r;
if (l == r) {
tree[p].s = a[l] - a[l - 1];
tree[p].g = a[l] - a[l - 1];
return;
}
int mid = (l + r) / 2;
int lc = 2 * p;
int rc = 2 * p + 1;
build(lc, l, mid);
build(rc, mid + 1, r);
tree[p].s = tree[lc].s + tree[rc].s;
tree[p].g = gcdd(tree[lc].g, tree[rc].g);
}
void update(int p, int x, long long v) {
if (tree[p].l == tree[p].r) {
tree[p].s += v;
tree[p].g += v;
return;
}
int mid = (tree[p].l + tree[p].r) / 2;
int lc = 2 * p;
int rc = 2 * p + 1;
if (x <= mid) {
update(lc, x, v);
} else {
update(rc, x, v);
}
tree[p].s = tree[lc].s + tree[rc].s;
tree[p].g = gcdd(tree[lc].g, tree[rc].g);
}
Tree query(int p, int l, int r) {
if (l <= tree[p].l && tree[p].r <= r) {
return tree[p];
}
int mid = (tree[p].l + tree[p].r) / 2;
int lc = 2 * p;
int rc = 2 * p + 1;
if (mid < l) {
return query(rc, l, r);
} else if (r <= mid) {
return query(lc, l, r);
} else {
Tree ll = query(lc, l, r);
Tree rr = query(rc, l, r);
Tree ret;
ret.s = ll.s + rr.s;
ret.g = gcdd(ll.g, rr.g);
return ret;
}
}
int main() {
scanf("%lld%lld", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
}
build(1, 1, n);
while (m--) {
char op[3];
scanf("%s", op);
int l, r;
if (op[0] == 'C') {
long long v;
scanf("%d%d%lld", &l, &r, &v);
update(1, l, v);
if (r + 1 <= n) {
update(1, r + 1, -v);
}
} else {
scanf("%d%d", &l, &r);
Tree ll = query(1, 1, l), rr;
if (l + 1 <= r) {
rr = query(1, l + 1, r);
} else {
rr = {0, 0, 0, 0};
}
printf("%lld\n", abs(gcdd(ll.s, rr.g)));
}
}
return 0;
}
----权值线段树
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10;
int n, a[N], op[N], b[N];
struct node {
int l, r, num;
};
node tree[8 * N];
void build(int p, int l, int r) {
tree[p].l = l;
tree[p].r = r;
tree[p].num = 0;
if (l == r) {
return;
}
int mid = (l + r) >> 1;
build(p * 2, l, mid);
build(p * 2 + 1, mid + 1, r);
}
void update(int p, int x, int v) {
if (tree[p].l == tree[p].r) {
tree[p].num += v;
return;
}
int mid = (tree[p].l + tree[p].r) >> 1;
if (x <= mid) {
update(p * 2, x, v);
} else {
update(p * 2 + 1, x, v);
}
tree[p].num = tree[p * 2].num + tree[p * 2 + 1].num;
}
int rankx1(int p, int x) {
if (tree[p].l == tree[p].r) {
return 1;
}
int mid = (tree[p].l + tree[p].r) >> 1;
if (x <= mid) {
return rankx1(p * 2, x);
} else {
return tree[p * 2].num + rankx1(p * 2 + 1, x);
}
}
int rankx2(int p, int x) {
if (tree[p].l == tree[p].r) {
return tree[p].num;
}
int mid = (tree[p].l + tree[p].r) >> 1;
if (x <= mid) {
return rankx2(p * 2, x);
} else {
return tree[p * 2].num + rankx2(p * 2 + 1, x);
}
}
int kth(int p, int k) {
if (tree[p].l == tree[p].r) {
return tree[p].l;
}
if (k <= tree[p * 2].num) {
return kth(p * 2, k);
} else {
return kth(p * 2 + 1, k - tree[p * 2].num);
}
}
signed main() {
scanf("%lld", &n);
for (int i = 1; i <= n; i++) {
scanf("%lld%lld", &op[i], &a[i]);
}
int tot = 0;
for (int i = 1; i <= n; i++) {
if (op[i] != 4) {
b[++tot] = a[i];
}
}
sort(b + 1, b + 1 + tot);
for (int i = 1; i <= n; i++) {
if (op[i] != 4) {
a[i] = lower_bound(b + 1, b + 1 + tot, a[i]) - b;
}
}
build(1, 1, tot);
for (int i = 1; i <= n; i++) {
if (op[i] == 1) {
update(1, a[i], 1);
} else if (op[i] == 2) {
update(1, a[i], -1);
} else if (op[i] == 3) {
printf("%lld\n", rankx1(1, a[i]));
} else if (op[i] == 4) {
printf("%lld\n", b[kth(1, a[i])]);
} else if (op[i] == 5) {
update(1, a[i], 1);
printf("%lld\n", b[kth(1, rankx1(1, a[i]) - 1)]);
update(1, a[i], -1);
} else {
update(1, a[i], 1);
printf("%lld\n", b[kth(1, rankx2(1, a[i]) + 1)]);
update(1, a[i], -1);
}
}
return 0;
}
----动态开点线段树
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10;
int n, m, a[N];
struct node {
int l, r, s, lazy, lc, rc;
};
node tree[2 * N];
int tot = 1, root = 1;
void build(int &p, int l, int r) {
if (!p) {
p = ++tot;
}
tree[p].l = l;
tree[p].r = r;
if (l == r) {
tree[p].s = a[l];
return;
}
int mid = (l + r) / 2;
build(tree[p].lc, l , mid);
build(tree[p].rc, mid + 1, r);
tree[p].s = tree[tree[p].lc].s + tree[tree[p].rc].s;
}
void pushdown(int p) {
if (tree[p].lazy && tree[p].l != tree[p].r) {
if (!tree[p].lc) tree[p].lc = ++tot;
if (!tree[p].rc) tree[p].rc = ++tot;
tree[tree[p].lc].lazy += tree[p].lazy;
tree[tree[p].rc].lazy += tree[p].lazy;
tree[tree[p].lc].s += tree[p].lazy * (tree[tree[p].lc].r - tree[tree[p].lc].l + 1);
tree[tree[p].rc].s += tree[p].lazy * (tree[tree[p].rc].r - tree[tree[p].rc].l + 1);
tree[p].lazy = 0;
}
}
void update(int &p, int l, int r, int v) {
if (!p) {
p = ++tot;
}
pushdown(p);
if (l <= tree[p].l && tree[p].r <= r) {
tree[p].lazy += v;
tree[p].s += v * (tree[p].r - tree[p].l + 1);
return;
}
int mid = (tree[p].l + tree[p].r) / 2;
if (r <= mid) {
update(tree[p].lc, l, r, v);
} else if (l > mid) {
update(tree[p].rc, l, r, v);
} else {
update(tree[p].lc, l, mid, v);
update(tree[p].rc, mid + 1, r, v);
}
tree[p].s = tree[tree[p].lc].s + tree[tree[p].rc].s;
}
int query(int p, int l, int r) {
if (!p) {
return 0;
}
pushdown(p);
if (l <= tree[p].l && tree[p].r <= r) {
return tree[p].s;
}
int mid = (tree[p].l + tree[p].r) / 2;
if (r <= mid) {
return query(tree[p].lc, l, r);
} else if (l > mid) {
return query(tree[p].rc, l , r);
} else {
return query(tree[p].lc, l, mid) + query(tree[p].rc, mid + 1, r);
}
}
signed main() {
for (int i = 0; i < N; i++) {
tree[i].lc = tree[i].rc = 0;
}
scanf("%lld%lld", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
}
build(root, 1, n);
while (m--) {
int op, x, y, k;
scanf("%lld%lld%lld", &op, &x, &y);
if (op == 1) {
scanf("%lld", &k);
update(root, x, y, k);
} else {
printf("%lld\n", query(1, x, y));
}
}
return 0;
}
----线段树合并
void merge(int &a, int b) {
if (!a) {
a = b;
return;
}
if (!b) {
return;
}
tree[a].num += tree[b].num;
merge(tree[a].lc, tree[b].lc);
merge(tree[a].rc, tree[b].rc);
}
----标记永久化
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10;
int n, m, a[N];
struct node {
int s, mark, lc, rc;
};
node tree[2 * N];
int tot = 0, root = 0;
void update(int &p, int l, int r, int x, int y, int v) {
if (!p) p = ++tot;
tree[p].s += v * (y - x + 1);
if (x == l && r == y) {
tree[p].mark += v;
return;
}
int mid = (l + r) / 2;
if (y <= mid) {
update(tree[p].lc, l, mid, x, y, v);
} else if (x > mid) {
update(tree[p].rc, mid + 1, r, x, y, v);
} else {
update(tree[p].lc, l, mid, x, mid, v);
update(tree[p].rc, mid + 1, r, mid + 1, y, v);
}
}
int query(int p, int l, int r, int x, int y, int ad) {
if (!p) return 0;
if (x == l && r == y) {
return tree[p].s + ad * (y - x + 1);
}
int mid = (l + r) / 2;
if (y <= mid) {
return query(tree[p].lc, l, mid, x, y, ad + tree[p].mark);
} else if (x > mid) {
return query(tree[p].rc, mid + 1, r, x, y, ad + tree[p].mark);
} else {
return query(tree[p].lc, l, mid, x, mid, ad + tree[p].mark)
+ query(tree[p].rc, mid + 1, r, mid + 1, y, ad + tree[p].mark);
}
}
signed main() {
scanf("%lld%lld", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
update(root, 1, n, i, i, a[i]);
}
while (m--) {
int op, x, y, k;
scanf("%lld%lld%lld", &op, &x, &y);
if (op == 1) {
scanf("%lld", &k);
update(root, 1, n, x, y, k);
} else {
printf("%lld\n", query(root, 1, n, x, y, 0));
}
}
return 0;
}
--分块
----区间修改区间求和
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10;
int n, m;
int a[N], st[N], ed[N], bel[N], sum[N], mark[N], siz[N];
void init() {
int sq = sqrt(n);
for (int i = 1; i <= sq; i++) {
st[i] = n / sq * (i - 1) + 1;
ed[i] = n / sq * i;
}
ed[sq] = n;
for (int i = 1; i <= sq; i++) {
siz[i] = ed[i] - st[i] + 1;
}
for (int i = 1; i <= sq; i++) {
for (int j = st[i]; j <= ed[i]; j++) {
bel[j] = i;
sum[i] += a[j];
}
}
}
void update(int x, int y, int v) {
if (bel[x] == bel[y]) {
for (int i = x; i <= y; i++) {
a[i] += v;
sum[bel[i]] += v;
}
} else {
for (int i = x; i <= ed[bel[x]]; i++) {
a[i] += v;
sum[bel[i]] += v;
}
for (int i = st[bel[y]]; i <= y; i++) {
a[i] += v;
sum[bel[i]] += v;
}
for (int i = bel[x] + 1; i < bel[y]; i++) {
mark[i] += v;
}
}
}
int query(int x, int y) {
int ans = 0;
if (bel[x] == bel[y]) {
for (int i = x; i <= y; i++) {
ans += a[i] + mark[bel[i]];
}
} else {
for (int i = x; i <= ed[bel[x]]; i++) {
ans += a[i] + mark[bel[i]];
}
for (int i = st[bel[y]]; i <= y; i++) {
ans += a[i] + mark[bel[i]];
}
for (int i = bel[x] + 1; i < bel[y]; i++) {
ans += sum[i] + mark[i] * siz[i];
}
}
return ans;
}
signed main() {
scanf("%lld%lld", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
}
init();
while (m--) {
int op;
scanf("%lld", &op);
if (op == 1) {
int x, y, k;
scanf("%lld%lld%lld", &x, &y, &k);
update(x, y, k);
} else {
int x, y;
scanf("%lld%lld", &x, &y);
printf("%lld\n", query(x, y));
}
}
return 0;
}
----区间修改区间求大于/小于/大于等于/小于等于
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 10;
int n, q, a[N], d[N];
int st[N], ed[N], bel[N], mark[N];
void init() {
int sq = sqrt(n);
for (int i = 1; i <= sq; i++) {
st[i] = n / sq * (i - 1) + 1;
ed[i] = n / sq * i;
}
ed[sq] = n;
for (int i = 1; i <= sq; i++) {
for (int j = st[i]; j <= ed[i]; j++) {
bel[j] = i;
}
}
for (int i = 1; i <= n; i++) {
d[i] = a[i];
}
for (int i = 1; i <= sq; i++) {
sort(d + st[i], d + ed[i] + 1);
}
}
void update(int x, int y, int v) {
if (bel[x] == bel[y]) {
for (int i = x; i <= y; i++) {
a[i] += v;
}
for (int i = st[bel[x]]; i <= ed[bel[x]]; i++) {
d[i] = a[i];
}
sort(d + st[bel[x]], d + ed[bel[x]] + 1);
return;
}
for (int i = x; i <= ed[bel[x]]; i++) {
a[i] += v;
}
for (int i = st[bel[x]]; i <= ed[bel[x]]; i++) {
d[i] = a[i];
}
sort(d + st[bel[x]], d + ed[bel[x]] + 1);
for (int i = st[bel[y]]; i <= y; i++) {
a[i] += v;
}
for (int i = st[bel[y]]; i <= ed[bel[y]]; i++) {
d[i] = a[i];
}
sort(d + st[bel[y]], d + ed[bel[y]] + 1);
for (int i = bel[x] + 1; i < bel[y]; i++) {
mark[i] += v;
}
}
int query(int x, int y, int v) {
int ans = 0;
if (bel[x] == bel[y]) {
for (int i = x; i <= y; i++) {
if (a[i] + mark[bel[i]] >= v) {
ans++;
}
}
return ans;
}
for (int i = x; i <= ed[bel[x]]; i++) {
if (a[i] + mark[bel[i]] >= v) {
ans++;
}
}
for (int i = st[bel[y]]; i <= y; i++) {
if (a[i] + mark[bel[i]] >= v) {
ans++;
}
}
for (int i = bel[x] + 1; i < bel[y]; i++) {
int l = st[i], r = ed[i], ret = 0;
while (l <= r) {
int mid = (l + r) / 2;
if (d[mid] + mark[i] >= v) {
ret = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
if (ret != 0) {
ans = ans + (ed[i] - ret + 1);
}
}
return ans;
}
signed main() {
scanf("%lld%lld", &n, &q);
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
}
init();
while (q--) {
char op[3];
int l, r, w;
scanf("%s%lld%lld%lld", op, &l, &r, &w);
if (op[0] == 'M') {
update(l, r, w);
} else {
printf("%lld\n", query(l, r, w));
}
}
return 0;
}
--ST表
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, M = 18;
int Log2[N];
int f[N][M]; // f[i][j] 表示区间 [i, 2^(j-1)-1] 的最大值
int n, m, a[N];
void init() {
for (int i = 2; i <= n; i++) Log2[i] = Log2[i / 2] + 1;
for (int i = 1; i <= n; i++) f[i][0] = a[i];
for (int j = 1; j <= Log2[n]; j++) {
for (int i = 1; i + (1 << j) - 1 <= n; i++) {
f[i][j] = max(f[i][j - 1], f[i + (1 << j - 1)][j - 1]); // ST表具体实现
}
}
}
int query(int l, int r) {
int s = Log2[r - l + 1];
return max(f[l][s], f[r - (1 << s) + 1][s]);
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
init();
while (m--) {
int l, r;
scanf("%d%d", &l, &r);
printf("%d\n", query(l, r));
}
return 0;
}
--平衡树
----FHQ-Treap
#include <bits/stdc++.h>
using namespace std;
mt19937 wdz(time(0));
const int N = 1e5 + 10;
int tot, root;
struct FHQ_Treap {
int l, r, val, key, siz;
} tr[N];
#define lc tr[p].l
#define rc tr[p].r
void update(int p) { // 更新 p 的子树大小
tr[p].siz = tr[lc].siz + tr[rc].siz + 1;
}
void create(int &p, int x) { // 新建一棵只有一个节点的 Treap
p = ++tot;
tr[p].val = x;
tr[p].key = wdz();
tr[p].siz = 1;
}
void split(int p, int k, int &x, int &y) {
// 根节点,关键值,以及分裂后的两个节点
if (!p) {
x = y = 0;
return;
}
if (tr[p].val <= k) { // 权值小于等于 k
x = p; // 左子树全部属于第一个子树
split(rc, k, rc, y); // 分裂右子树
} else {
y = p;
split(lc, k, x, lc);
}
update(p);
}
int merge(int x, int y) { // 返回合并后的树根
if (!x || !y) {
return x + y;
}
if (tr[x].key < tr[y].key) { // x 的优先级小于 y 的优先级
tr[x].r = merge(tr[x].r, y);
// 将子树 y 并入子树 x 的右子树
update(x);
return x;
} else {
tr[y].l = merge(x, tr[y].l);
update(y);
return y;
}
}
int kth(int p, int k) { // 查询在 p 及其子树中排名为 k 的数
if (k == tr[lc].siz + 1) { // 为当前节点
return tr[p].val;
}
if (k <= tr[lc].siz) { // 在左子树中
return kth(lc, k);
} else { // 在右子树中
return kth(rc, k - tr[lc].siz - 1);
}
}
int qq;
int now, tmp, x, y;
int main() {
scanf("%d", &qq);
while (qq--) {
int op, k;
scanf("%d%d", &op, &k);
if (op == 1) {
// 插入 k
split(root, k, x, y);
create(now, k);
root = merge(merge(x, now), y);
} else if (op == 2) {
// 删除 k
split(root, k, x, tmp);
split(x, k - 1, x, y);
// 分离子树
y = merge(tr[y].l, tr[y].r);
// 合并 x 的子树,也就是去掉 x
root = merge(merge(x, y), tmp);
} else if (op == 3) {
// 查询 k 数的排名
split(root, k - 1, x, y); // 分离子树
printf("%d\n", tr[x].siz + 1); // 节点数即为排名
root = merge(x, y);
} else if (op == 4) {
// 查询排名为 k 的数
printf("%d\n", kth(root, k));
} else if (op == 5) {
// 前驱
split(root, k - 1, x, y);
printf("%d\n", kth(x, tr[x].siz));
root = merge(x, y);
} else {
// 后继
split(root, k, x, y);
printf("%d\n", kth(y, 1));
root = merge(x, y);
}
}
return 0;
}
图论
--tarjan
----强联通分量
vector<int> G[N];
int dfn[N], low[N], inst[N], scc[N], siz[N];
stack<int> st;
int tot = 0, cnt = 0;
void tarjan(int x) {
dfn[x] = low[x] = ++tot;
inst[x] = 1;
st.push(x);
for (int y : G[x]) {
if (dfn[y] == 0) {
tarjan(y);
low[x] = min(low[x], low[y]);
} else if (inst[y] == 1) {
low[x] = min(low[x], dfn[y]);
}
}
if (low[x] == dfn[x]) {
int y;
cnt++;
while (true) {
y = st.top();
st.pop();
inst[y] = 0;
scc[y] = cnt;
siz[cnt]++;
if (x == y) {
break;
}
}
}
}
----缩点
同上,略
----割点
int n;
vector<int> G[N];
int dfn[N], low[N], ans[N];
int tot = 0;
void tarjan(int x, int fa) {
int son = 0;
low[x] = dfn[x] = ++tot;
for (int y : G[x]) {
if (dfn[y] == 0) {
son++;
tarjan(y, x);
low[x] = min(low[x], low[y]);
if (low[y] >= dfn[x]) {
ans[x] = 1;
}
} else if (y != fa) {
low[x] = min(low[x], dfn[y]);
}
}
if (fa == 0 && son < 2) {
ans[x] = 0;
}
}
----割边
int n, m;
vector<int> G[N];
int dfn[N], low[N], ans[N][N];
int tot = 0;
void tarjan(int x, int fa) {
low[x] = dfn[x] = ++tot;
for (int y : G[x]) {
if (dfn[y] == 0) {
tarjan(y, x);
low[x] = min(low[x], low[y]);
if (low[y] > dfn[x]) {
ans[x][y] = ans[y][x] = 1;
}
} else if (y != fa) {
low[x] = min(low[x], dfn[y]);
}
}
}
----点双
#include <bits/stdc++.h>
using namespace std;
const int N = 300 + 10;
int n, m;
vector<int> G[N];
int dfn[N], low[N];
stack<int> st;
vector<int> dcc[N];
int cnt = 0;
int tot = 0;
void tarjan(int x, int fa) {
low[x] = dfn[x] = ++tot;
st.push(x);
if (fa == 0 && G[x].size() == 0) {
dcc[++cnt].push_back(x);
return;
}
for (int y : G[x]) {
if (dfn[y] == 0) {
tarjan(y, x);
low[x] = min(low[x], low[y]);
if (low[y] >= dfn[x]) {
cnt++;
while (true) {
int z = st.top();
st.pop();
dcc[cnt].push_back(z);
if (y == z) {
break;
}
}
dcc[cnt].push_back(x);
}
} else if (y != fa) {
low[x] = min(low[x], dfn[y]);
}
}
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) {
int x, y;
scanf("%d%d", &x, &y);
if (x != y) {
G[x].push_back(y);
G[y].push_back(x);
}
}
for (int i = 1; i <= n; i++) {
if (dfn[i] == 0) {
tarjan(i, 0);
}
}
printf("%d\n", cnt);
for (int i = 1; i <= cnt; i++) {
printf("%d ", dcc[i].size());
for (int x : dcc[i]) {
printf("%d ", x);
}
printf("\n");
}
return 0;
}
----边双
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
int n, m;
vector<int> G[N];
int dfn[N], low[N];
stack<int> st;
vector<int> dcc[N];
int cnt = 0;
int tot = 0;
void tarjan(int x, int fa) {
low[x] = dfn[x] = ++tot;
st.push(x);
for (int y : G[x]) {
if (dfn[y] == 0) {
tarjan(y, x);
low[x] = min(low[x], low[y]);
if (low[y] > dfn[x]) {
}
} else if (y != fa) {
low[x] = min(low[x], dfn[y]);
}
}
if (dfn[x] == low[x]) {
cnt++;
while (true) {
int y = st.top();
st.pop();
dcc[cnt].push_back(y);
if (x == y) {
break;
}
}
}
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) {
int x, y;
scanf("%d%d", &x, &y);
if (x != y) {
G[x].push_back(y);
G[y].push_back(x);
}
}
for (int i = 1; i <= n; i++) {
if (dfn[i] == 0) {
tarjan(i, 0);
}
}
printf("%d\n", cnt);
for (int i = 1; i <= cnt; i++) {
printf("%d ", dcc[i].size());
for (int x : dcc[i]) {
printf("%d ", x);
}
printf("\n");
}
return 0;
}
--lca
void dfs(int x, int father) {
dep[x] = dep[father] + 1;
fa[x][0] = father;
for (int i = 1; i <= 19; i++) {
fa[x][i] = fa[fa[x][i-1]][i-1];
}
for (int y : G[x]) {
if (y != father) {
dfs(y, x);
}
}
}
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 y;
}
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];
}
--prim
vector<int> G[N];
int w[N][N], dis[N], vis[N];
struct node {
int x, d;
bool operator < (const node &cmp) const {
return d > cmp.d;
}
};
priority_queue<node> q;
int ans = 0;
void prim() {
dis[1] = 0;
node tmp;
tmp.x = 1;
tmp.d = 0;
q.push(tmp);
int cnt = 0;
while (!q.empty()) {
node f = q.top();
q.pop();
int x = f.x;
if (f.d > dis[x]) {
continue;
}
cnt++;
ans += f.d;
dis[x] = 1;
for (int y : G[x]) {
if (vis[y] == 0 && w[x][y] < dis[y]) {
dis[y] = w[x][y];
node t;
t.x = y;
t.d = dis[y];
q.push(t);
}
}
}
if (cnt < n) ans = -1;
}
int main() {
memset(w, INF, sizeof(w));
memset(dis, INF, sizeof(dis));
return 0;
}
--kruskal
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 5e3 + 10, M = 2e5 + 10;
int n, m;
struct edge {
int x, y, w;
};
edge e[M];
int fa[N];
int fid(int x) {
if (x != fa[x]) {
fa[x] = fid(fa[x]);
}
return fa[x];
}
bool cmp(edge a, edge b) {
return a.w < b.w;
}
int ans = 0;
void kruskal() {
int cnt = 0;
sort(e + 1, e + 1 + m, cmp);
for (int i = 1; i <= m; i++) {
int x = fid(e[i].x);
int y = fid(e[i].y);
if (x == y) continue;
ans += e[i].w;
fa[y] = x;
cnt++;
if (cnt == n - 1) {
return;
}
}
ans = -1;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) fa[i] = i;
for (int i = 1; i <= m; i++) {
scanf("%d%d%d", &e[i].x, &e[i].y, &e[i].w);
}
kruskal();
if (ans == -1) {
printf("orz\n");
} else {
printf("%d\n", ans);
}
return 0;
}
--dijkstra
struct edge {
int y, w, id;
};
vector<edge> G[N];
int dis[N], pre[N];
struct node {
int x, d;
bool operator < (const node & cmp) const {
return d > cmp.d;
}
};
priority_queue<node> q;
void dijkstra() {
for (int i = 2; i <= n; i++) dis[i] = INF, pre[i] = 0;
q.push({1, 0});
while (!q.empty()) {
node f = q.top(); q.pop();
if (f.d > dis[f.x]) continue;
for (edge i : G[f.x]) {
if (dis[i.y] > f.d + i.w) {
dis[i.y] = f.d + i.w;
pre[i.y] = i.id;
q.push({i.y, dis[i.y]});
}
}
}
}
--拓补排序
// 直接复制的OI-WIKI上的,懒得打了
bool toposort() {
vector<int> L;
queue<int> S;
for (int i = 1; i <= n; i++)
if (in[i] == 0) S.push(i);
while (!S.empty()) {
int u = S.front();
S.pop();
L.push_back(u);
for (auto v : G[u]) {
if (--in[v] == 0) {
S.push(v);
}
}
}
if (L.size() == n) {
for (auto i : L) cout << i << ' ';
return true;
} else {
return false;
}
}
--树链剖分
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10;
int n, m, rt, MOD;
vector<int> G[N];
int w[N], fa[N], dep[N], siz[N], son[N], id[N], rk[N], top[N];
int cnt = 0;
void dfs1(int x, int father) {
fa[x] = father;
dep[x] = dep[father] + 1;
siz[x] = 1;
for (int y : G[x]) {
if (y != father) {
dfs1(y, x);
siz[x] += siz[y];
if (siz[y] > siz[son[x]]) {
son[x] = y;
}
}
}
}
void dfs2(int x, int t) {
id[x] = ++cnt;
rk[cnt] = x;
top[x] = t;
if (son[x] == 0) return;
dfs2(son[x], t);
for (int y : G[x]) {
if (y != fa[x] && y != son[x]) {
dfs2(y, y);
}
}
}
struct node {
int l, r, sum, lazy;
};
node tree[4 * N];
void build(int p, int l, int r) {
tree[p].l = l, tree[p].r = r;
tree[p].lazy = 0;
if (l == r) {
tree[p].sum = w[rk[l]];
return;
}
int mid = (l + r) / 2;
int lc = p * 2, rc = p * 2 + 1;
build(lc, l, mid);
build(rc, mid + 1, r);
tree[p].sum = tree[lc].sum + tree[rc].sum;
}
void pushdown(int p) {
if (tree[p].lazy && tree[p].l != tree[p].r) {
int lc = p * 2, rc = p * 2 + 1;
tree[lc].lazy += tree[p].lazy;
tree[rc].lazy += tree[p].lazy;
tree[lc].sum += tree[p].lazy * (tree[lc].r - tree[lc].l + 1);
tree[rc].sum += tree[p].lazy * (tree[rc].r - tree[rc].l + 1);
tree[p].lazy = 0;
}
}
void update(int p, int l, int r, int v) {
pushdown(p);
if (l == tree[p].l && tree[p].r == r) {
tree[p].sum += v * (r - l + 1);
tree[p].lazy += v;
return;
}
int mid = (tree[p].l + tree[p].r) / 2;
int lc = p * 2, rc = p * 2 + 1;
if (r <= mid) {
update(lc, l, r, v);
} else if (l > mid) {
update(rc, l, r, v);
} else {
update(lc, l, mid, v);
update(rc, mid + 1, r, v);
}
tree[p].sum = tree[lc].sum + tree[rc].sum;
}
void update_path(int x, int y, int v) {
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
update(1, id[top[x]], id[x], v);
x = fa[top[x]];
}
if (dep[x] < dep[y]) swap(x, y);
update(1, id[y], id[x], v);
}
void update_tree(int x, int v) {
update(1, id[x], id[x] + siz[x] - 1, v);
}
int query(int p, int l, int r) {
pushdown(p);
if (l == tree[p].l && tree[p].r == r) {
return tree[p].sum;
}
int mid = (tree[p].l + tree[p].r) / 2;
int lc = p * 2, rc = p * 2 + 1;
if (r <= mid) {
return query(lc, l, r);
} else if (l > mid) {
return query(rc, l, r);
} else {
return query(lc, l, mid) + query(rc, mid + 1, r);
}
}
int query_path(int x, int y) {
int ret = 0;
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
ret += query(1, id[top[x]], id[x]);
x = fa[top[x]];
}
if (dep[x] < dep[y]) swap(x, y);
ret += query(1, id[y], id[x]);
return ret;
}
int query_tree(int x) {
return query(1, id[x], id[x] + siz[x] - 1);
}
signed main() {
scanf("%lld%lld%lld%lld", &n, &m, &rt, &MOD);
for (int i = 1; i <= n; i++) {
scanf("%lld", &w[i]);
}
for (int i = 1; i < n; i++) {
int x, y;
scanf("%lld%lld", &x, &y);
G[x].push_back(y);
G[y].push_back(x);
}
dfs1(rt, 0);
dfs2(rt, rt);
build(1, 1, n);
while (m--) {
int op;
scanf("%lld", &op);
if (op == 1) {
int x, y, z;
scanf("%lld%lld%lld", &x, &y, &z);
update_path(x, y, z);
} else if (op == 2) {
int x, y;
scanf("%lld%lld", &x, &y);
printf("%lld\n", query_path(x, y) % MOD);
} else if (op == 3) {
int x, z;
scanf("%lld%lld", &x, &z);
update_tree(x, z);
} else {
int x;
scanf("%lld", &x);
printf("%lld\n", query_tree(x) % MOD);
}
}
return 0;
}
--树的直径
void dfs1(int x, int fa, int flag) {
dis[x] = dis[fa] + 1;
if (!flag)
u = dis[x] > dis[u] ? x : u;
else {
v = dis[x] > dis[v] ? x : v;
pre[x] = fa;
}
for (int y : G[x]) {
if (y == fa) dfs1(y, x);
}
}
杂项
--快读
char buf[1 << 20], *p1, *p2;
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 20, stdin), p1 == p2) ? EOF : *p1++)
int read() {
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') {
f = -1;
}
ch = getchar();
}
while ('0' <= ch && ch <= '9') {
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
--快写
void write(int x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
--离散化
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
b[i] = a[i];
}
sort(b + 1, b + 1 + n);
int ll = unique(b + 1, b + 1 + n) - (b + 1);
for (int i = 1; i <= n; i++) {
a[i] = lower_bound(b + 1, b + 1 + ll, a[i]) - b;
}
--莫队
----普通莫队
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5e4 + 10;
int n, m, k, block, bel[N], a[N], t[N], ans[N], l, r, num;
struct node {
int l, r, id;
};
node modui[N];
bool cmp(node a, node b) {
if ((a.l - 1) / block != (b.l - 1) / block) {
return (a.l - 1) / block < (b.l - 1) / block;
}
return a.r < b.r;
}
void add(int i) {
num = num + 2 * t[a[i]] + 1;
t[a[i]]++;
}
void del(int i) {
num = num - 2 * t[a[i]] + 1;
t[a[i]]--;
}
signed main() {
scanf("%lld%lld%lld", &n, &m, &k);
block = sqrt(n);
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
}
for (int i = 1; i <= m; i++) {
scanf("%lld%lld", &modui[i].l, &modui[i].r);
modui[i].id = i;
}
sort(modui + 1, modui + 1 + m, cmp);
l = 1, r = 0, num = 0;
for (int i = 1; i <= m; i++) {
while (l > modui[i].l) l--, add(l);
while (r < modui[i].r) r++, add(r);
while (l < modui[i].l) del(l), l++;
while (r > modui[i].r) del(r), r--;
ans[modui[i].id] = num;
}
for (int i = 1; i <= m; i++) {
printf("%lld\n", ans[i]);
}
return 0;
}
----带修莫队
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 10;
int n, m, a[N], block, qcnt, rcnt, l, r, num, last, t[N], ans[N];
char op[3];
struct qnode {
int l, r, t, id;
} q[N];
struct rnode {
int p, x;
} c[N];
bool cmp(qnode a, qnode b) {
if ((a.l - 1) / block != (b.l - 1) / block) {
return (a.l - 1) / block < (b.l - 1) / block;
}
if ((a.r - 1) / block != (b.r - 1) / block) {
return (a.r - 1) / block < (b.r - 1) / block;
}
return a.t < b.t;
}
void add(int x) {
if (t[x] == 0) num++;
t[x]++;
}
void del(int x) {
t[x]--;
if (t[x] == 0) num--;
}
signed main() {
scanf("%lld%lld", &n, &m);
block = pow(n, 2 / 3.0);
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
}
for (int i = 1; i <= m; i++) {
int x, y;
scanf("%s%lld%lld", op, &x, &y);
if (op[0] == 'Q') {
q[++qcnt] = {x, y, rcnt, qcnt};
} else {
c[++rcnt] = {x, y};
}
}
sort(q + 1, q + 1 + qcnt, cmp);
l = 1, r = 0, num = 0, last = 0;
for (int i = 1; i <= qcnt; i++) {
while (l > q[i].l) l--, add(a[l]);
while (r < q[i].r) r++, add(a[r]);
while (l < q[i].l) del(a[l]), l++;
while (r > q[i].r) del(a[r]), r--;
while (last < q[i].t) {
last++;
if (q[i].l <= c[last].p && c[last].p <= q[i].r) {
del(a[c[last].p]);
add(c[last].x);
}
swap(a[c[last].p], c[last].x);
}
while (last > q[i].t) {
if (q[i].l <= c[last].p && c[last].p <= q[i].r) {
del(a[c[last].p]);
add(c[last].x);
}
swap(a[c[last].p], c[last].x);
last--;
}
ans[q[i].id] = num;
}
for (int i = 1; i <= qcnt; i++) {
printf("%lld\n", ans[i]);
}
return 0;
}
----回滚莫队
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10;
int n, q, a[N], b[N], bel[N], st[N], ed[N], l, r, t[N], mp[N], ans[N], last, num;
struct node {
int l, r, id;
};
node modui[N];
bool cmp(node a, node b) {
if (bel[a.l] != bel[b.l]) {
return bel[a.l] < bel[b.l];
}
return a.r < b.r;
}
void add(int x) {
t[x]++;
if (b[x] * t[x] > num) {
num = b[x] * t[x];
}
}
signed main() {
scanf("%lld%lld", &n, &q);
int sq = sqrt(n);
for (int i = 1; i <= sq; i++) {
st[i] = n / sq * (i - 1) + 1;
ed[i] = n / sq * i;
}
ed[sq] = n;
for (int i = 1; i <= sq; i++) {
for (int j = st[i]; j <= ed[i]; j++) {
bel[j] = i;
}
}
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
b[i] = a[i];
}
sort(b + 1, b + 1 + n);
int m = unique(b + 1, b + 1 + n) - b - 1;
for (int i = 1; i <= n; i++) {
a[i] = lower_bound(b + 1, b + 1 + m, a[i]) - b;
}
for (int i = 1; i <= q; i++) {
scanf("%lld%lld", &modui[i].l, &modui[i].r);
modui[i].id = i;
}
sort(modui + 1, modui + 1 + q, cmp);
l = 1, r = 0, last = 0;
for (int i = 1; i <= q; i++) {
if (bel[modui[i].l] == bel[modui[i].r]) {
for (int j = modui[i].l; j <= modui[i].r; j++) {
mp[a[j]]++;
}
int mx = 0;
for (int j = modui[i].l; j <= modui[i].r; j++) {
if (b[a[j]] * mp[a[j]] > mx) {
mx = b[a[j]] * mp[a[j]];
}
mp[a[j]] = 0;
}
ans[modui[i].id] = mx;
continue;
}
if (bel[modui[i].l] != last) {
while (r > ed[bel[modui[i].l]]) t[a[r]]--, r--;
while (l < ed[bel[modui[i].l]] + 1) t[a[l]]--, l++;
num = 0;
last = bel[modui[i].l];
}
int tmpl = l;
while (r < modui[i].r) r++, add(a[r]);
int tmp = num;
while (l > modui[i].l) l--, add(a[l]);
ans[modui[i].id] = num;
num = tmp;
while (l < tmpl) t[a[l]]--, l++;
}
for (int i = 1; i <= q; i++) {
printf("%lld\n", ans[i]);
}
return 0;
}
--CDQ分治
void cdq(int l, int r) {
if(l == r) return ;
int mid = (l + r) >> 1;
cdq(l, mid);
cdq(mid + 1, r);
int pl = l, pr = mid + 1;
for(int i = l; i <= r; i++) {
if((p[pl].y <= p[pr].y && pl <= mid) || (pr > r)) {
b[i] = p[pl++];
}
else {
p[pr].num += pl - l;
b[i] = p[pr++];
}
}
for(int i = l; i <= r; i++) {
p[i] = b[i];
}
}
--高维前缀和
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
for(int k = 1; k <= n; k++)
a[i][j][k] += a[i - 1][j][k];
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
for(int k = 1; k <= n; k++)
a[i][j][k] += a[i][j - 1][k];
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
for(int k = 1; k <= n; k++)
a[i][j][k] += a[i][j][k - 1];
就是数学那块加了一些公式之类的,以及整体的格式进行了更改,看起来更加规整。

“至尊高坐天中,四海皆在目下。”——神鲁肃。右边这幅图正是神鲁肃。九州为局,天下之势尽在此处。这篇文章《板子》我认为还是很棒的。还是涵盖了很多内容的。
浙公网安备 33010602011771号