【高精度】
算法模板
高精度加法
// 结构体
struct BIG
{
int len, num[1005];
BIG() // 构造函数,初始化高精度数
{
memset(num, 0, sizeof(num));
len = 1;
}
void set(int n) // 整数转高精度
{
memset(num, 0, sizeof(num));
len = 0;
while (n > 0)
{
num[++len] = n % 10;
n /= 10;
}
if (len == 0)
{
len = 1;
}
}
void set(string s) // 字符串转高精度
{
memset(num, 0, sizeof(num));
len = s.size();
for (int i = 1; i <= len; i++)
{
num[i] = s[len - i] - '0';
}
}
void print() // 输出高精度数
{
for (int i = len; i >= 1; i--)
{
cout << num[i];
}
cout << endl;
}
};
// 加法
BIG operator+(BIG a, BIG b) // 重载加法运算符
{
BIG c; // 存储结果的变量
c.len = max(a.len, b.len); // 结果的初始长度为较大数的长度
int u = 0; // 进位值,初始为0
for (int i = 1; i <= c.len; i++) // 从低位到高位逐位相加
{
int t = a.num[i] + b.num[i] + u; // 当前位的和
c.num[i] = t % 10; // 取个位作为当前位结果
u = t / 10; // 计算进位
}
if (u > 0) // 如果最后还有进位
c.num[++c.len] = u; // 增加一位存储进位
return c; // 返回相加结果
}
高精度减法
// 高精度比较
bool operator<(BIG a, BIG b) // 小于比较
{
if (a.len != b.len)
{
return a.len < b.len;
}
for (int i = a.len; i >= 1; i--)
{
if (a.num[i] != b.num[i])
{
return a.num[i] < b.num[i];
}
}
return false;
}
// 减法
BIG operator - (BIG a, BIG b) // 高精度减法(假设a >= b)
{
BIG c;
c.len = max(a.len, b.len);
int u = 0; // 借位
for (int i = 1; i <= c.len; i++)
{
int t = a.num[i] - b.num[i] - u;
if (t < 0)
{
c.num[i] = t + 10;
u = 1;
}
else
{
c.num[i] = t;
u = 0;
}
}
while (c.num[c.len] == 0 && c.len > 1) // 去除前导零
{
c.len--;
}
return c;
}
高精度乘法
// 乘法
BIG operator*(BIG a, int b) // 高精度乘以整数
{
BIG c;
c.len = a.len;
int u = 0; // 进位
for (int i = 1; i <= c.len; i++)
{
int t = a.num[i] * b + u;
c.num[i] = t % 10;
u = t / 10;
}
while (u > 0) // 处理最后的进位
{
c.num[++c.len] = u % 10;
u /= 10;
}
return c;
}
高精度除法
BIG operator/(BIG a, int b) // 高精度除以整数(整除)
{
BIG c;
c.len = a.len;
int r = 0; // 记录余数
for (int i = c.len; i >= 1; i--) // 从最高位开始除
{
int t = r * 10 + a.num[i];
c.num[i] = t / b;
r = t % b;
}
while (c.num[c.len] == 0 && c.len > 1) // 去除前导零
{
c.len--;
}
return c;
}
高精度求余
int mod(BIG a, int div)
{
long long rem = 0; // 余数
for (int i = a.len; i >= 1; i--) // 从高位到低位
rem = (rem * 10 + a.num[i]) % div; // 模拟除法
return (int)rem;
}
//如果rem为0说明是s是div的倍数
算法应用
【整数二分】
算法模板
二分查找
lower_bound(a+1, a+n+1, x); // 第一个 >= x 的位置
upper_bound(a+1, a+n+1, x); // 第一个 > x 的位置
upper_bound(a+1, a+n+1, x) - 1; // 最后一个 <= x 的位置
upper_bound(a+1, a+n+1, x) - lower_bound(a+1, a+n+1, x); // 等于x的个数
二分答案
// [l,r]区间寻找最小的最大值
int l = 0, r = 1e18;
while (l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid; // check()判断mid是否满足性质
else l = mid + 1;
}
// l就是答案
// [l, r]区间寻找最大的最小值
while (l < r)
{
int mid = l + r + 1 >> 1; //当l=mid时,一定要l+r+1
if (check(mid)) l = mid;
else r = mid - 1;
}
// l就是答案
算法应用
| 题目来源 |
标题 |
难度 |
星级 |
考察算法 |
一句话思路总结 |
| AcWing |
503 借教室 |
|
|
|
|
| AcWing |
789 数的范围 |
简单 |
⭐⭐ |
整数二分查找(两次二分) |
在升序数组中两次二分查找目标元素的左右边界:第一次找左边界(第一个 \(\ge x\) 的位置),mid = (l+r)>>1,若 q[mid] >= x 则 r = mid,否则 l = mid+1;若 q[l] != x 则输出 -1 -1;第二次找右边界(最后一个 \(\le x\) 的位置),mid = (l+r+1)>>1(注意 \(+1\) 防止死循环),若 q[mid] <= x 则 l = mid,否则 r = mid-1;每次查询时间复杂度 \(O(\log n)\)。 |
| AcWing |
1227 分巧克力 |
|
|
|
|
| AcWing |
[[4927 河流跳房子]] |
|
|
|
|
| AcWing |
5963 查找最接近的元素 |
|
|
|
|
| AcWing |
5965 网线主管 |
|
|
|
|
| AcWing |
5966 月度开销 |
|
|
|
|
| AcWing |
5967 和为给定数 |
|
|
|
|
| 洛谷 |
AT_abc443_b Setsubun |
入门 |
|
|
|
| 洛谷 |
AT_abc334_d Reindeer and Sleigh |
普及- |
|
|
|
| 洛谷 |
AT_abc355_c Bingo 2 |
普及- |
|
|
|
| 洛谷 |
AT_abc358_d Souvenirs |
普及- |
|
|
|
| 洛谷 |
AT_abc365_c Transportation Expenses |
普及- |
|
|
|
| 洛谷 |
AT_abc376_c Prepare Another Box |
普及- |
|
|
|
| 洛谷 |
AT_abc382_c Kaiten Sushi |
普及- |
|
|
|
| 洛谷 |
AT_abc388_c Various Kagamimochi |
普及- |
|
|
|
| 洛谷 |
AT_abc463_c Tallest at the Moment |
普及- |
|
|
|
| 洛谷 |
CF670C Cinema |
普及- |
|
|
|
| 洛谷 |
P1102 A-B数对 |
普及- |
|
|
|
| 洛谷 |
P1678 烦恼的高考志愿 |
普及- |
|
|
|
| 洛谷 |
[[P1824 Aggressive cows]] |
普及- |
|
|
|
| 洛谷 |
P1873 砍树 |
普及- |
|
|
|
| 洛谷 |
P2249 查找 |
普及- |
|
|
|
| 洛谷 |
P2969 Music Notes |
普及- |
|
|
|
| 洛谷 |
[[P9937 Acowdemia I]] |
普及- |
|
|
|
| 洛谷 |
U218625 网线主管 |
普及- |
|
|
|
| 洛谷 |
AT_abc319_d Minimum Width |
普及 |
|
|
|
| 洛谷 |
AT_abc360_d Ghost Ants |
普及 |
|
|
|
| 洛谷 |
AT_abc415_e Hungry Takahashi |
普及 |
|
|
|
| 洛谷 |
AT_abc463_d Maximize the Gap |
普及 |
⭐⭐⭐ |
二分答案 + 贪心判定 |
按右端点升序排序后,二分答案范围为 \([0, 10^9]\),判定函数 check(mid) 贪心选择右端点最小的布作为起点,遍历后续布:若当前布左端点与上一块选中布右端点的距离 \(\geq mid\) 则选中该布并更新 last 为当前右端点;若最终选出不少于 \(K\) 块布则 mid 可行;二分取上中点 mid = (l+r+1)/2 防死循环,若 check(mid) 为真则 l=mid 尝试更大值,否则 r=mid-1;最终若 l=0 输出 \(-1\),否则输出 \(l\),时间复杂度 \(O(N \log N + N \log MAX)\)。 |
| 洛谷 |
[[B4107 刷题]] |
普及 |
|
|
|
| 洛谷 |
P1083 借教室 |
普及 |
|
|
|
| 洛谷 |
P1111 修复公路 |
普及 |
|
|
|
| 洛谷 |
P1182 数列分段 |
普及 |
|
|
|
| 洛谷 |
P1281 书的复制 |
普及 |
|
|
|
| 洛谷 |
P1902 刺杀大使 |
普及 |
|
|
|
| 洛谷 |
P2440 木材加工 |
普及 |
|
|
|
| 洛谷 |
P2678 跳石头 |
普及 |
|
|
|
| 洛谷 |
P3743 kotori的设备 |
普及 |
|
|
|
| 洛谷 |
P3853 路标设置 |
普及 |
|
|
|
| 洛谷 |
P9125 Cow-libi |
普及 |
|
|
|
| 洛谷 |
[[P9948 Race]] |
普及 |
|
|
|
| 洛谷 |
[[P10719 黑白格]] |
普及 |
|
|
|
| 洛谷 |
P13013 奖品兑换 |
普及 |
⭐⭐⭐ |
整数二分 + 贪心调整 |
设兑换 \(mid\) 份奖品,每份需 \(a\) 张券1和 \(b\) 张券2(或互换),二分答案后用贪心调整:若券1不足则将部分"券1为主"的方案换成"券2为主"(每次调整节省 \(a-b\) 张券1、多花 \(a-b\) 张券2),检查调整后两种券均不超限制,时间复杂度 \(O(\log \min(n,m))\),空间复杂度 \(O(1)\) |
| 洛谷 |
P14917 数字移动 |
普及 |
|
|
|
| 洛谷 |
AT_abc385_d Santa Claus 2 |
普及+ |
|
|
|
| 洛谷 |
AT_abc417_d Takahashi's Expectation |
普及+ |
|
|
|
| 洛谷 |
P1314 聪明的质监员 |
普及+ |
|
|
|
| 洛谷 |
[[P3611 Cow Dance Show]] |
普及+ |
|
|
|
| 洛谷 |
[[P6004 Wormhole Sort]] |
普及+ |
|
|
|
| 洛谷 |
P9124 Bakery |
普及+ |
|
|
|
| 洛谷 |
AT_abc389_e Square Price |
提高+ |
|
|
|
| 洛谷 |
P4090 Greedy Gift Takers |
提高+ |
|
|
|
| 洛谷 |
P10451 Innovative Business |
提高+ |
|
|
|
| AtCoder |
AT_awc0005_d Splitting Delivery Packages |
|
⭐⭐⭐ |
二分答案 + 贪心判定 |
二分答案范围为 \([0, \sum A_i]\),判定函数 check(x) 贪心地将数组从左到右分割:维护当前段和 sum,若 sum + a[i] >= x 则段数 ans++ 并重置 sum=0(尽可能早结束当前段),否则 sum += a[i];若最终 ans >= K 则 x 可行;二分取 mid = (l+r+1)/2 防死循环,若 check(mid) 为真则 l=mid 尝试更大值,否则 r=mid-1;最终 l 即为最大可行最小载重,时间复杂度 \(O(N \log S)\)。 |
| AtCoder |
AT_awc0027_c Building a Wireless Network |
|
|
|
|
| AtCoder |
AT_awc0032_c Observatory with a Mountain View |
|
|
|
|
| AtCoder |
AT_awc0035_b Package Distribution Center |
|
|
|
|
| AtCoder |
AT_awc0036_c Splitting Logs |
|
|
|
|
| AtCoder |
AT_awc0046_b Organizing the Locker |
|
|
|
|
| AtCoder |
AT_awc0046_d Operation Plan for Distribution Center |
|
⭐⭐⭐ |
排序 + 前缀和 + 枚举端点 + 二分定位 |
将期望日期排序并求前缀和后,枚举每个 \(D_i\) 作为区间左端点(\(l=D_i, r=l+K-1\))或右端点(\(r=D_i, l=r-K+1\)),用 lower_bound / upper_bound 二分定位区间内元素范围,左侧代价为 \((left-1)\cdot l - sd[left-1]\),右侧代价为 \((sd[n]-sd[right])-(n-right)\cdot r\),取所有情况最小值,时间复杂度 \(O(N \log N)\)。 |
| AtCoder |
AT_awc0080_b Quality Inspection and Product Disposal |
|
⭐⭐ |
排序 + 二分查找 + 双指针 |
将质量分数升序排序后,用指针 \(st\) 维护当前已处理到的位置,每次查询用 upper_bound 找到第一个 \(\geq T_j\) 的位置 \(pos\),若 \(pos \leq st\) 则新销毁数为 \(0\),否则为 \(pos-st\),随后更新 \(st = \max(st, pos)\),时间复杂度 \(O(N \log N + Q \log N)\)。 |
| AtCoder |
AT_awc0082_d Corridor Doors and Hit Points |
|
⭐⭐⭐⭐ |
二分答案 + 等差数列计数 + 前缀和 |
对每种安全模式用等差数列公式 \(O(1)\) 计算时刻 \(t\) 时门 \(x\) 的锁数量(首项 \(first = ((r \bmod a) + (v \bmod a) \cdot (t \bmod a)) \bmod a\),修正为 \(a\) 若余数为 \(0\),则 \(\leq idx\) 的锁数为 \(\lfloor\frac{idx-first}{a}\rfloor+1\)),对每个查询分别向左右二分:向左在 \([0,S_j]\) 找最远边界使区间锁数 \(\leq P_j\)(下取整中点),向右在 \([S_j,L-1]\) 同理(上取整中点防死循环),最终答案为左扩房间数 + 右扩房间数 \(-1\),时间复杂度 \(O(Q \cdot N \cdot \log L)\)。 |
| AtCoder |
AT_awc0098_b Library Book Lending |
|
|
|
|
| AtCoder |
AT_awc0101_b A Single Strike of Dominoes |
|
⭐⭐⭐ |
整数二分 + 模拟验证 |
对施加的力 \(X\) 进行二分搜索(范围 \([1, 10^9]\)),每次用 check(x) 模拟:先将所有柱子耐久度减去 \(X\),然后从左到右遍历,若第 \(i\) 根柱子倒塌(耐久度 \(\le 0\))则第 \(i+1\) 根额外减 \(1\),最后判断是否全部倒塌,根据 check 结果调整二分边界,时间复杂度 \(O(N \log A_{\max})\),空间复杂度 \(O(N)\)。 |
| 学而思编程 |
买整数 |
普及进阶 |
⭐⭐ |
整数二分 |
对能购买的最大整数进行整数二分,验证函数 check(x) 计算数字 \(x\) 的位数后判断 a*x + b*位数 <= k 是否成立,利用价格关于 \(x\) 的单调性二分查找满足条件的最大 \(x\),边界为 \([0, 10^9]\),时间复杂度 \(O(\log(10^9) \cdot \log x)\)(每次 check 需 \(O(\log x)\) 计算位数)。 |
| 学而思编程 |
射击王 |
普及进阶 |
⭐⭐⭐⭐ |
整数二分 + 贪心排序 |
对最终得分 \(mid\) 进行整数二分,check(mid) 计算每个气球在得分不超过 \(mid\) 的前提下最晚可射击时间 t[i] = (mid - h[i]) / s[i],将所有 t[i] 升序排序后贪心验证:第 \(i\) 个气球必须在时间 \(i-1\) 之前射击(从第 0 秒开始),即需满足 \(t[i] \ge i-1\),二分查找满足条件的最小 \(mid\),时间复杂度 \(O(n \log n \cdot \log(\max \text{ans}))\)(\(n \le 10^5\))。 |
| 学而思编程 |
展览会 |
普及进阶 |
⭐⭐⭐⭐ |
整数二分 + 贪心验证 |
对"华丽度"(选出画中最低价值)进行整数二分,验证函数 check(mid) 按位置排序后从左到右贪心选择:若当前画价值 \(\ge mid\) 且与上一幅选中画的距离 \(\ge D\) 则选中,最终判断是否能选出至少 \(m\) 幅画,二分查找最大的可行华丽度,时间复杂度 \(O(n \log n + n \log V)\)(\(n \le 10^5, V\) 为最大价值)。 |
| 学而思编程 |
折半与最小值 |
普及进阶 |
⭐⭐⭐⭐ |
整数二分 + 贪心验证 |
对答案 \(x\) 进行整数二分,验证函数 check(x) 贪心计算每根木棒 \(a_i\) 不断二分后能得到的最少片段数(当片段长度 \(\ge x\) 时停止分割),若总片段数 \(\ge n+m\) 则 \(x\) 可行,二分查找最大的可行 \(x\),时间复杂度 \(O(T \cdot n \cdot \log(\max a_i) \cdot \log a_i)\)(\(T\) 为测试用例数,\(n \le 10^5, a_i \le 10^9\))。 |
【浮点二分】
算法模板
double l = 0, r = 1000;
double eps = 1e-6; // eps 表示精度,取决于题目对精度的要求,需要比预留的小数位+2
while (r - l > eps)
{
double mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid;
}
return l;
算法应用
【三分】
算法模板
算法应用
【前缀和】
算法模板
一维前缀和
// 前缀和求解
for (int i=1; i<=n; i++)
s[i] = s[i-1] + a[i];
printf("%d\n", s[r]-s[l-1]);
二维前缀和
//预处理s[i,j]
for (int i=1; i<=n; i++)
for (int j=1; j<=m; j++)
s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + a[i][j];
//询问
while (q--) {
int x1, y1, x2, y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
printf("%d\n", s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]+s[x1-1][y1-1]);
}
算法应用
【树上前缀和】
算法模板
算法应用
| 题目来源 |
标题 |
难度 |
星级 |
考察算法 |
一句话思路总结 |
| 洛谷 |
P4427 求和 |
普及+ |
|
|
|
【差分】
算法模板
一维差分
// 获得差分数组
for (int i=1; i<=n; i++) b[i] = a[i]-a[i-1];
// for (int i=1; i<=n; i++) insert(i, i, a[i]);
// 给区间[l, r]中的每个数加上c:
void insert(int l, int r, int c)
{
b[l]+=c;
b[r+1]-=c;
}
// 还原差分数组为原数组
for (int i=1; i<=n; i++) b[i] += b[i-1];
二维差分
//预处理出b[i][j]差分数组
for (int i=1; i<=n; i++)
for (int j=1; j<=m; j++)
insert(i, j, i, j, a[i][j]);
// 给以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵中的所有元素加上c:
void insert(int x1, int y1, int x2, int y2, int c)
{
b[x1][y1] += c;
b[x2+1][y1] -= c;
b[x1][y2+1] -= c;
b[x2+1][y2+1] += c;
}
//通过前缀和还原数组
for (int i=1; i<=n; i++)
for (int j=1; j<=m; j++)
b[i][j] += b[i-1][j] + b[i][j-1] - b[i-1][j-1];
算法应用
【树上差分】
算法模板
void dfs2(int u, int f)
{
for (int i=0; i<h[u].size(); i++)
{
int j = h[u][i];
if (j==f) continue;
dfs2(j, u);
power[u] += power[j];
}
}
for (int i=1; i<=a; i++)
{
int x, y;
cin >> x >> y;
int l = lca(x, y);
++power[x]; ++power[y];
--power[l]; --power[fa[l][0]];
}
dfs2(1, 0);
算法应用
| 题目来源 |
标题 |
难度 |
星级 |
考察算法 |
一句话思路总结 |
| AcWing |
352 闇の連鎖 |
困难 |
⭐⭐⭐⭐⭐ |
树上差分 + LCA(倍增) |
以 \(N-1\) 条主要边构建树并预处理倍增数组求LCA后,对每条附加边 \((u,v)\) 执行树上差分(\(d[u]++\)、\(d[v]++\)、\(d[lca]-=2\)),再通过DFS后序遍历累加子树差分值得到每条主要边被覆盖的次数 \(s\):若 \(s=0\) 则贡献 \(M\) 种方案(需切断任意附加边),若 \(s=1\) 则贡献 \(1\) 种方案(需切断对应那条附加边),若 \(s \geq 2\) 则贡献 \(0\) 种方案,累加所有方案数得答案,时间复杂度 \(O((N+M) \log N)\)。 |
| 洛谷 |
P3128 Max Flow |
普及+ |
⭐⭐⭐⭐ |
树上差分 + LCA(倍增) |
预处理树的深度和倍增数组求LCA后,对每条路径 \((s,t)\) 执行树上差分:\(power[s]++\)、\(power[t]++\)、\(power[lca]--\)、\(power[fa[lca][0]]--\),最后通过一次DFS将子节点的权值累加到父节点,统计每个隔间的压力并取最大值,时间复杂度 \(O((N+K) \log N)\)。 |
【ST表】
算法模板
// 预处理ST表
for (int j = 1; j <= 20; j++) // 枚举区间长度(2^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]);
}
}
// 处理查询
for (int i = 1; i <= m; i++)
{
int l, r;
scanf("%d%d", &l, &r); // 输入查询区间
// 计算区间长度的对数
int k = log2(r - l + 1);
// 查询区间最大值:取两个覆盖区间的最大值
printf("%d\n", max(f[l][k], f[r - (1 << k) + 1][k]));
}
算法应用
| 题目来源 |
标题 |
难度 |
星级 |
考察算法 |
一句话思路总结 |
| 洛谷 |
[[P2880 Balanced Lineup]] |
普及 |
|
|
|
| 洛谷 |
P3865 ST表 & RMQ问题 |
普及 |
⭐⭐ |
ST表(稀疏表) |
预处理 \(f[i][j]\) 表示区间 \([i, i+2^j-1]\) 的最大值,状态转移 \(f[i][j] = \max(f[i][j-1], f[i+2^{j-1}][j-1])\),查询时取 \(k = \lfloor\log_2(r-l+1)\rfloor\),答案为 \(\max(f[l][k], f[r-2^k+1][k])\),预处理 \(O(N \log N)\),单次查询 \(O(1)\)。 |
| 洛谷 |
P7333 JFCA |
普及+ |
|
|
|
| 洛谷 |
P8818 策略游戏 |
普及+ |
|
|
|
| 洛谷 |
P10059 Choose |
普及+ |
|
|
|
【快速排序】
算法模板
void qsort(int l, int r)
{ // 对区间[l, r]进行快速排序
int x = a[(l+r)/2], i=l, j = r; // 选择中间的元素作为基准值
while (i<=j)
{
while (a[i]<x) i++; // 在x左边从左到右找到第一个>=x的数字a[i]
while (a[j]>x) j--; // 在x右边从右到左找到第一个<=x的数字a[j]
// 如果找到的两个元素的下标没有交错,交换这两个元素,并移动下标
if (i<=j) swap(a[i], a[j]), i++, j--;
}
if (l<j) qsort(l, j); // 递归地对左半部分进行快速排序
if (i<r) qsort(i, r); // 递归地对右半部分进行快速排序
}
算法应用
【归并排序】
算法模板
void merge_sort(int l, int r)
{
if (l==r) return; // 如果子数组只有一个元素,则不需要排序,直接返回
int mid = (l + r) / 2 ;
merge_sort(l, mid); // 递归地对左半部分进行归并排序
merge_sort(mid+1, r); // 递归地对右半部分进行归并排序
int x = l, y = mid+1, cnt = l-1;
while (x<=mid && y<=r) // 只要左右两端子数组都不为空
{
if (a[x]<=a[y]) // 将两段子数组左端点较小者插入b数组
{
b[++cnt] = a[x];
x++;
}
else
{
b[++cnt] = a[y];
y++;
}
}
// 合并过程中其中一个子数组的元素已全部合并完成,而另一个子数组仍有剩余元素的情况
for (int i=x; i<=mid; i++) b[++cnt] = a[i]; // 如果左半部分还有剩余的元素,将它们复制到b中
for (int i=y; i<=r; i++) b[++cnt] = a[i]; // 如果右半部分还有剩余的元素,将它们复制到b中
for (int i=l; i<=r; i++) a[i] = b[i]; // 将b中已经排序好的部分复制回a中,完成合并
}
算法应用
| 题目来源 |
标题 |
难度 |
星级 |
考察算法 |
一句话思路总结 |
| AcWing |
787 归并排序 |
简单 |
⭐⭐ |
归并排序(分治 + 双指针合并) |
递归将数组二分至单元素区间,然后用双指针合并两个有序子数组(取较小者放入临时数组,处理剩余元素后拷贝回原数组),时间复杂度严格 \(O(n \log n)\),空间复杂度 \(O(n)\)。 |
| AcWing |
788 逆序对的数量 |
|
|
|
|
| AcWing |
5910 求逆序对 |
|
|
|
|
| AcWing |
5958 光荣的梦想 |
|
|
|
|
| AcWing |
5960 输出前k大的数 |
|
|
|
|
| AcWing |
5962 求排列的逆序数 |
|
|
|
|
| 洛谷 |
P1908 逆序对 |
普及- |
⭐⭐⭐ |
归并排序 / 离散化 + 树状数组 |
方法一(归并排序):分治过程中合并两个有序子数组时,若右半部分元素 \(a[j]\) 小于左半部分元素 \(a[i]\),则左半部分从 \(i\) 到 \(mid\) 的所有元素都与 \(a[j]\) 构成逆序对,累加 \(mid-i+1\) 到答案,时间复杂度 \(O(n \log n)\);方法二(树状数组):将数值离散化后,从左到右遍历,用树状数组维护已出现元素频率,每次查询已出现且比当前大的元素数量(\(query(n)-query(newval)\))即为以当前元素结尾的逆序对数,时间复杂度 \(O(n \log n)\)。 |
| 洛谷 |
P1966 火柴排队 |
普及+ |
|
|
|
【堆排序】
算法模板
基本声明方式
// 整型大根堆
priority_queue<int, vector<int> > pq;
// 整型小根堆
priority_queue<int, vector<int>, greater<int> > pq;
// 结构体中符号重载,< 表示大的在前,> 表示小的在前(对应greater)
struct Node
{
int val;
// 自定义符号重载,< 表示大的在前,> 表示小的在前(对应greater)
// bool operator<(const Node& other) const {
// return val < other.val; // 大的在前
// }
};
// 结构体类型大根堆
priority_queue<Node, vector<Node> > pq;
// 在结构体外定义符号重载,< 表示大的在前,> 表示小的在前(对应greater)
bool operator<(Node a, Node b) // 表示大的在前
{
return a.val < b.val;
}
核心操作
// 插入元素
pq.push(x);
// 删除元素
pq.pop();
// 取队首元素
int top = pq.top();
// 判断堆是否为空
if (pq.empty()) { ... }
// 取堆的大小
int size = pq.size();
算法应用
【其他排序】
算法模板
struct Node
{
int t, d; // 定义结构体成员
}a[N];
bool cmp(Node x, Node y)
{
if (x.t != y.t) return x.t < y.t; // 使用不等的方式来求保证优先排序
return x.d < y.d;
}
算法应用
【双指针】
算法模板
基本声明方式(for+while)
for (int i = 0, j = 0; i < n; i ++ )
{
while (j < i && check(i, j)) j ++ ; // 这里while的内容也跟着题目要求变化
// 具体问题的逻辑
}
基本声明方式(while)
while (i<=n && j<=m)
{
if (...)
{ // 同时移动
i++;
j++;
...
}
else // 单个指针移动(是i移动或j移动根据题意而不同)
j++;
}
算法应用
【贪心】
算法模板
// 贪心没有固定的模板,但一般都涉及到排序
// 大部分贪心可以先假设,再验证,少量贪心需要数学公式证明,例如 a*b 或 a/b 等等
算法应用
| 题目来源 |
标题 |
难度 |
星级 |
考察算法 |
一句话思路总结 |
| AcWing |
104 货仓选址 |
|
|
|
|
| AcWing |
125 耍杂技的牛 |
|
|
|
|
| AcWing |
148 合并果子 |
简单 |
⭐⭐⭐ |
贪心 + 哈夫曼编码(小根堆) |
将所有果子数量放入小根堆,每次取出最小的两堆合并(代价累加为 \(x+y\)),再将合并后的新堆放回堆中,重复 \(n-1\) 次直到只剩一堆,总代价即为最小体力耗费,时间复杂度 \(O(n \log n)\)。 |
| AcWing |
442 接水问题 |
|
|
|
|
| AcWing |
803 区间合并 |
简单 |
⭐⭐ |
贪心 + 排序 |
将所有区间按左端点升序排序后线性扫描,维护当前合并区间的起止点 \((st,ed)\):若新区间左端点 \(l > ed\) 则将 \([st,ed]\) 加入结果并开始新区间,否则合并为 \(ed = \max(ed,r)\),最后处理最后一个区间,输出结果数量,时间复杂度 \(O(n \log n)\)。 |
| AcWing |
905 区间选点 |
|
|
|
|
| AcWing |
906 区间分组 |
|
|
|
|
| AcWing |
907 区间覆盖 |
|
|
|
|
| AcWing |
908 最大不相交区间数量 |
|
|
|
|
| AcWing |
913 排队打水 |
|
|
|
|
| AcWing |
1536 均分纸牌 |
|
|
|
|
| AcWing |
3360 牛棚 |
|
|
|
|
| AcWing |
[[4171 智力大冲浪]] |
|
|
|
|
| AcWing |
5942 排队接水 |
|
|
|
|
| AcWing |
5943 删数问题 |
|
|
|
|
| AcWing |
5944 活动选择 |
|
|
|
|
| AcWing |
5945 整数区间 |
|
|
|
|
| AcWing |
5946 一个简单问题 |
|
|
|
|
| AcWing |
5947 金银岛 |
|
|
|
|
| AcWing |
5948 装箱问题 |
|
|
|
|
| AcWing |
5949 骑车上班 |
|
|
|
|
| AcWing |
5950 书架 |
|
|
|
|
| AcWing |
5951 电池的寿命 |
|
|
|
|
| AcWing |
5952 寻找平面上的极大点 |
|
|
|
|
| AcWing |
5953 最小新整数 |
|
|
|
|
| AcWing |
5954 过河 |
|
|
|
|
| AcWing |
5961 区间合并 |
|
|
|
|
| 洛谷 |
AT_abc373_c Max Ai+Bj |
入门 |
|
|
|
| 洛谷 |
[[P1639 Teleportation]] |
入门 |
|
|
|
| 洛谷 |
AT_abc332_c T-shirts |
普及- |
|
|
|
| 洛谷 |
AT_abc352_c Standing On The Shoulders |
普及- |
|
|
|
| 洛谷 |
AT_abc360_c Move It |
普及- |
|
|
|
| 洛谷 |
AT_abc361_c Make Them Narrow |
普及- |
|
|
|
| 洛谷 |
AT_abc362_c Sum = 0 |
普及- |
|
|
|
| 洛谷 |
AT_abc364_c Minimum Glutton |
普及- |
|
|
|
| 洛谷 |
AT_abc370_c Word Ladder |
普及- |
|
|
|
| 洛谷 |
AT_abc396_c Buy Balls |
普及- |
|
|
|
| 洛谷 |
AT_abc416_b 1D Akari |
普及- |
|
|
|
| 洛谷 |
AT_abc421_c Alternated |
普及- |
|
|
|
| 洛谷 |
AT_abc422_c AtCoder AAC Contest |
普及- |
|
|
|
| 洛谷 |
AT_abc423_c Lock All Doors |
普及- |
|
|
|
| 洛谷 |
AT_abc441_c Sake or Water |
普及- |
|
|
|
| 洛谷 |
AT_abc461_c Variety |
普及- |
|
|
|
| 洛谷 |
[[B3959 做题]] |
普及- |
|
|
|
| 洛谷 |
B4051 小杨的武器 |
普及- |
|
|
|
| 洛谷 |
[[B4359 分糖果]] |
普及- |
|
|
|
| 洛谷 |
B4377 交换字符 |
普及- |
|
|
|
| 洛谷 |
P1031 均分纸牌 |
普及- |
|
|
|
| 洛谷 |
P1094 纪念品分组 |
普及- |
|
|
|
| 洛谷 |
P1109 学生分组 |
普及- |
|
|
|
| 洛谷 |
P1190 接水问题 |
普及- |
|
|
|
| 洛谷 |
P1208 混合牛奶 |
普及- |
|
|
|
| 洛谷 |
P1223 排队接水 |
普及- |
|
|
|
| 洛谷 |
[[P1376 机器工厂]] |
普及- |
|
|
|
| 洛谷 |
[[P1459 三值的排序]] |
普及- |
|
|
|
| 洛谷 |
P1478 涛涛摘苹果(升级版) |
普及- |
|
|
|
| 洛谷 |
P1496 火烧赤壁 |
普及- |
|
|
|
| 洛谷 |
P1803 线段覆盖 |
普及- |
|
|
|
| 洛谷 |
P2240 部分背包问题 |
普及- |
|
|
|
| 洛谷 |
P2616 Buying Feed, II |
普及- |
|
|
|
| 洛谷 |
P2676 Bookshelf |
普及- |
|
|
|
| 洛谷 |
[[P2909 Cow Cars]] |
普及- |
|
|
|
| 洛谷 |
[[P2945 Sand Castle]] |
普及- |
|
|
|
| 洛谷 |
[[P3650 滑雪课程设计]] |
普及- |
|
|
|
| 洛谷 |
P3817 小A的糖果 |
普及- |
|
|
|
| 洛谷 |
[[P4379 Lemonade Line]] |
普及- |
|
|
|
| 洛谷 |
P4995 跳跳 |
普及- |
|
|
|
| 洛谷 |
P5690 日期 |
普及- |
|
|
|
| 洛谷 |
P6244 Country Fair Events |
普及- |
|
|
|
| 洛谷 |
P7427 玩游戏 |
普及- |
|
|
|
| 洛谷 |
P7666 寒冬暖炉 |
普及- |
|
|
|
| 洛谷 |
P8597 翻硬币 |
普及- |
|
|
|
| 洛谷 |
P8697 最长子序列 |
普及- |
|
|
|
| 洛谷 |
P8739 重复字符串 |
普及- |
|
|
|
| 洛谷 |
P8837 商店 |
普及- |
|
|
|
| 洛谷 |
[[P9121 Hungry Cow]] |
普及- |
|
|
|
| 洛谷 |
[[P9123 Watching Mooloo]] |
普及- |
|
|
|
| 洛谷 |
P9749 公路 |
普及- |
|
|
|
| 洛谷 |
P9868 词典 |
普及- |
|
|
|
| 洛谷 |
P10452 货仓选址 |
普及- |
|
|
|
| 洛谷 |
AT_abc334_c Socks 2 |
普及 |
|
|
|
| 洛谷 |
AT_abc347_c Ideal Holidays |
普及 |
|
|
|
| 洛谷 |
AT_abc386_d Diagonal Separation |
普及 |
|
|
|
| 洛谷 |
AT_abc399_d Switch Seats |
普及 |
|
|
|
| 洛谷 |
AT_abc401_d Logical Filling |
普及 |
|
|
|
| 洛谷 |
AT_abc408_d Flip to Gather |
普及 |
|
|
|
| 洛谷 |
AT_abc409_d String Rotation |
普及 |
|
|
|
| 洛谷 |
AT_abc412_c Giant Domino |
普及 |
|
|
|
| 洛谷 |
AT_abc414_d Transmission Mission |
普及 |
|
|
|
| 洛谷 |
AT_abc415_d Get Many Stickers |
普及 |
|
|
|
| 洛谷 |
AT_abc416_d Match, Mod, Minimize 2 |
普及 |
|
|
|
| 洛谷 |
[[B3872 巧夺大奖]] |
普及 |
|
|
|
| 洛谷 |
[[B3930 烹饪问题]] |
普及 |
|
|
|
| 洛谷 |
B4071 武器强化 |
普及 |
|
|
|
| 洛谷 |
CF1486B Eastern Exhibition |
普及 |
|
|
|
| 洛谷 |
P1007 独木桥 |
普及 |
|
|
|
| 洛谷 |
P1016 旅行家的预算 |
普及 |
|
|
|
| 洛谷 |
P1056 排座椅 |
普及 |
|
|
|
| 洛谷 |
P1090 合并果子 |
普及 |
|
|
|
| 洛谷 |
P1095 守望者的逃离 |
普及 |
|
|
|
| 洛谷 |
P1106 删数问题 |
普及 |
|
|
|
| 洛谷 |
P1158 导弹拦截 |
普及 |
|
|
|
| 洛谷 |
[[P1167 刷题]] |
普及 |
|
|
|
| 洛谷 |
P1199 三国游戏 |
普及 |
|
|
|
| 洛谷 |
[[P1209 修理牛棚]] |
普及 |
|
|
|
| 洛谷 |
[[P1230 智力大冲浪]] |
普及 |
|
|
|
| 洛谷 |
[[P1338 末日的传说]] |
普及 |
|
|
|
| 洛谷 |
[[P1694 The Great Revegetation]] |
普及 |
|
|
|
| 洛谷 |
[[P1842 奶牛玩杂技]] |
普及 |
|
|
|
| 洛谷 |
P1843 奶牛晒衣服 |
普及 |
|
|
|
| 洛谷 |
P1969 积木大赛 |
普及 |
|
|
|
| 洛谷 |
P2878 Protecting the Flowers |
普及 |
⭐⭐⭐ |
自定义排序贪心 + 后缀和 |
通过交换论证推导出按 \(T_i/D_i\) 升序排序(即 \(T_i \times D_j < T_j \times D_i\) 时 \(i\) 先赶),排序后预处理后缀和 \(sa[i]\) 表示从第 \(i\) 个任务到最后的总时间,总代价为 \(\sum_{i=1}^{n} 2 \times D_i \times sa[i+1]\),时间复杂度 \(O(n \log n)\)。 |
| 洛谷 |
[[P2902 Pearl Pairing]] |
普及 |
|
|
|
| 洛谷 |
[[P3078 Poker Hands]] |
普及 |
|
|
|
| 洛谷 |
P5019 铺设道路 |
普及 |
|
|
|
| 洛谷 |
P7076 动物园 |
普及 |
|
|
|
| 洛谷 |
[[P7994 Air Cownditioning]] |
普及 |
|
|
|
| 洛谷 |
[[P9975 Cowntact Tracing 2]] |
普及 |
|
|
|
| 洛谷 |
P11229 小木棍 |
普及 |
|
|
|
| 洛谷 |
[[P11247 算法学习]] |
普及 |
|
|
|
| 洛谷 |
[[P11960 平均分配]] |
普及 |
|
|
|
| 洛谷 |
P14359 异或和 |
普及 |
|
|
|
| 洛谷 |
P14361 社团招新 |
普及 |
|
|
|
| 洛谷 |
P15256 Purchasing Milk |
普及 |
⭐⭐⭐⭐ |
贪心 + 二进制分解 + 价格预处理 |
预处理价格使 \(a_i = \min(a_i, 2 \cdot a_{i-1})\) 保证合理性,并记录 \(i \geq 32\) 时的最低价格作为保底;对每个查询 \(x\),从大到小枚举前31个交易(\(base = 2^{i-1}\)),贪心购买 \(x/base\) 个当前交易,计算候选答案(有余数则多买一个),同时更新剩余需求 \(x \%= base\),最终输出最小花费,单次查询 \(O(\log x)\)。 |
| 洛谷 |
P1080 国王游戏 |
普及+ |
|
|
|
| 洛谷 |
P1809 过河问题 |
普及+ |
⭐⭐⭐ |
贪心 |
将过河时间升序排序后,从后往前每次送最慢的两人过河,比较两种方案取最小:方案1(最快+次快过去→最快回来→最慢+次慢过去→次快回来,耗时 \(t_2+t_1+t_{idx}+t_2\))与方案2(最快+最慢过去→最快回来→最快+次慢过去→最快回来,耗时 \(t_{idx-1}+t_1+t_{idx}+t_1\)),剩余2人直接一起过,剩余3人特殊处理,时间复杂度 \(O(N \log N)\)。 |
| 洛谷 |
P2114 起床困难综合征 |
普及+ |
|
|
|
| 洛谷 |
[[P2376 Allowance]] |
普及+ |
|
|
|
| 洛谷 |
[[P3173 巧克力]] |
普及+ |
|
|
|
| 洛谷 |
P4016 负载平衡问题 |
普及+ |
⭐⭐⭐⭐ |
贪心 + 中位数 |
计算平均值 \(avg\) 后,从第一个仓库开始贪心传递:第 \(i\) 个仓库需要调整 \(t = avg - a[i]\),将差额传给下一个仓库并记录传递量 \(b[i+1] = t\),累加 $\ |
| 洛谷 |
P4447 分组 |
普及+ |
|
|
|
| 洛谷 |
P5687 网格图 |
普及+ |
|
|
|
| 洛谷 |
P6033 合并果子 加强版 |
普及+ |
|
|
|
| 洛谷 |
P7915 回文 |
普及+ |
|
|
|
| 洛谷 |
P10134 Cowmpetency |
普及+ |
|
|
|
| 洛谷 |
P11232 超速检测 |
普及+ |
|
|
|
| 洛谷 |
P1315 观光公交 |
提高+ |
|
|
|
| 洛谷 |
P2512 糖果传递 |
提高+ |
|
|
|
| 洛谷 |
P2672 推销员 |
提高+ |
|
|
|
| 洛谷 |
P3076 Taxi |
提高+ |
|
|
|
| 洛谷 |
P4053 建筑抢修 |
提高+ |
|
|
|
| 洛谷 |
P11361 编辑字符串 |
提高+ |
|
|
|
| 洛谷 |
[[AT_abc466_d Placing Rooks]] |
|
|
|
|
| AtCoder |
AT_awc0001_c Discount Coupon |
|
|
|
|
| AtCoder |
AT_awc0003_c Bargain Sale Selection |
|
|
|
|
| AtCoder |
AT_awc0004_c Minimum Cost of Temperature Adjustment |
|
|
|
|
| AtCoder |
AT_awc0004_d Parking Lot Assignment |
|
⭐⭐⭐ |
贪心 + 排序 |
将车辆按右端点 \(R_i\) 升序排序(\(R_i\) 相同则按 \(L_i\) 升序),然后贪心遍历每辆车,在其区间 \([L_i, R_i]\) 内从左到右扫描分配第一个空闲停车位,若某辆车无法分配则输出 No,否则全部分配成功输出 Yes,时间复杂度 \(O(M \log M + N \times M)\)。 |
| AtCoder |
AT_awc0006_d Placement of Security Guards |
|
⭐⭐⭐ |
贪心(区间覆盖) |
将所有区间按左端点升序排序后,维护当前已覆盖的最右位置 \(r\),每次在左端点不超过 \(r+1\) 的区间中选择右端点最大的区间扩展覆盖范围,若某步无法扩展或出现断层(\(L_i > r+1\))则输出 \(-1\),否则覆盖到 \(N\) 后输出使用区间数,时间复杂度 \(O(M \log M)\)。 |
| AtCoder |
AT_awc0010_d Rock Destruction |
|
|
|
|
| AtCoder |
AT_awc0012_c Team Formation |
|
|
|
|
| AtCoder |
AT_awc0013_c Optimal Pairing for a Tournament |
|
|
|
|
| AtCoder |
AT_awc0014_a Loading Cargo |
|
⭐ |
贪心 / 模拟 |
每辆卡车独立计算,重量限制下最多装载 \(\lfloor T / P_i \rfloor\) 件,数量限制为 \(R\) 件,取两者较小值 \(r_i = \min(R, \lfloor T / P_i \rfloor)\) 即为最大装载量,时间复杂度 \(O(N)\)。 |
| AtCoder |
AT_awc0019_c Consecutive Card Distribution |
|
|
|
|
| AtCoder |
AT_awc0019_e Loading Cargo |
|
⭐ |
模拟 |
对每辆卡车独立计算重量限制下的最大件数 \(\lfloor T / P_i \rfloor\),再与数量上限 \(R\) 取较小值即得该卡车最大装载量 \(r_i = \min(R, T / P_i)\),时间复杂度 \(O(N)\)。 |
| AtCoder |
AT_awc0020_c Organizing the Bookshelf |
|
|
|
|
| AtCoder |
AT_awc0020_d Wi-Fi Spot Connection |
|
|
|
|
| AtCoder |
AT_awc0021_c Bargain Shopping Mall Tour |
|
|
|
|
| AtCoder |
AT_awc0025_c Air Conditioning Installation in Classrooms |
|
|
|
|
| AtCoder |
AT_awc0026_c Field Trip Snacks |
|
|
|
|
| AtCoder |
AT_awc0028_c Make-Up Exam |
|
|
|
|
| AtCoder |
AT_awc0029_c Formation of the Strongest Pair |
|
|
|
|
| AtCoder |
AT_awc0031_d Library Inventory Check |
|
⭐⭐⭐⭐ |
贪心 + 排序 + 二分查找 |
将每天检查上限 \(L\) 升序排序、书籍需求 \(R\) 降序排序后,遍历每本书累加需求 \(sumr\),同时用 lower_bound 找到第一个 \(L \geq i\) 的位置,累加能力达标的天数 \(suml\),若某时刻 \(suml < sumr\) 则输出 No,否则遍历完输出 Yes,时间复杂度 \(O(N \log N + M \log M + M \log N)\)。 |
| AtCoder |
AT_awc0033_b Plant Temperature Management |
|
|
|
|
| AtCoder |
AT_awc0046_c Seating Arrangement |
|
⭐⭐⭐ |
贪心 |
从左到右遍历每个空座位,检查三种冲突情况:左右均占 \((s_{i-1}=1,s_{i+1}=1)\)、左侧连续两占 \((s_{i-2}=1,s_{i-1}=1)\)、右侧连续两占 \((s_{i+1}=1,s_{i+2}=1)\),若均不冲突则安排客人并标记 \(s_i=1\),累加答案,时间复杂度 \(O(N)\)。 |
| AtCoder |
AT_awc0047_d Equal Distribution of Cake |
|
|
|
|
| AtCoder |
AT_awc0087_b Factory Machine Maintenance |
|
|
|
|
| AtCoder |
AT_awc0087_d Returning Library Books |
|
⭐⭐⭐ |
贪心 + 分组 |
将书架坐标按正负分离,正数降序排序、负数升序排序(均为绝对值从大到小),每次取最远的 \(K\) 本书为一组处理,每组往返距离为 \(2 \times\) 该组最远距离,累加所有组距离得最小总移动距离,时间复杂度 \(O(N \log N)\)。 |
| AtCoder |
AT_awc0089_b Connecting Pipes |
|
|
|
|
| 学而思编程 |
乘积最大 |
普及进阶 |
⭐⭐⭐⭐ |
贪心(数字分配 + 数位调整) |
将数字各位从大到小排序后循环分配到三个数中,若位数不是 3 的倍数则补 0 使三数位数相同;贪心调整策略为:让最大数 v[1] 尽可能小(与 v[3] 交换使 v[1] 变小),同时让后两个数 v[2] 和 v[3] 的差值尽可能小(确定大小关系后交换使较小者变大),最终三个数的乘积最大,时间复杂度 \(O(d^2)\)(\(d\) 为数字位数,\(d \le 18\))。 |
| 学而思编程 |
排队 |
普及奠基 |
⭐⭐ |
贪心 / 排序 |
按耐心指数升序排序,依次尝试安排,若当前已安排人数 \(\leq a_i\) 则该学生留下,否则跳过。 |
【反悔贪心】
算法模板
算法应用
【位运算】
算法模板
// 求n的第k位数字(0或1):
n >> k & 1
// 如果一个整数需要转换为二进制,可用标准的方式,但是如果转为的二进制有固定长度,高位补0,则可以
for (int i = l - 1; i >= 0; i--) // 从最高位到最低位输出二进制结果
cout << ((ans >> i) & 1LL); // 取出 ans 的第 i 位并输出
// 返回n的最后一位1:
lowbit(n) = n & -n
算法应用
【递推】
算法模板
// 最经典的递推,斐波那契数列
f[n] = f[n-1] + f[n-2];
// 递推题可以在摸索到数字规律后使用 f[] 的公式形式来表达
算法应用
【离散化】
算法模板
结构体方式离散化
//对原有数组去重
struct Node
{
int val, pos, newval;
}a[N];
bool cmp(Node x, Node y)
{
return x.val < y.val;
}
bool cmp2(Node x, Node y)
{
return x.pos < y.pos;
}
for (int i=1; i<=n; i++)
{
cin >> a[i].val;
a[i].pos = i;
}
sort(a+1, a+n+1, cmp);
int cnt = 0;
for (int i=1; i<=n; i++)
{
if (a[i].val!=a[i-1].val)
cnt++;
a[i].newval = cnt;
}
sort(a+1, a+n+1, cmp2); // 根据pos,从小到大还原
// 以下为示例
/*
7
10 9 7 11 10 8 6
1 10 5
2 9 4
3 7 2
4 11 6
5 10 5
6 8 3
7 6 1
14
*/
vector方式离散化
vector<int> alls; // 存储所有需要离散化的坐标
// 离散化处理:排序并去重
sort(alls.begin(), alls.end());
alls.erase(unique(alls.begin(), alls.end()), alls.end());
// 处理插入操作(离散化后)
for (auto item : add)
{
int x = find(item.first); // 获取离散化后的坐标
a[x] += item.second; // 在对应位置累加值
}
int find(double y)
{
return lower_bound(alls.begin(), alls.end(), y) - alls.begin();
}
int find(int x)
{
int l = 0, r = alls.size() - 1;
while (l < r)
{
int mid = (l + r) >> 1;
if (alls[mid] >= x)
r = mid;
else
l = mid + 1;
}
return r + 1; // 返回离散化后的索引(从1开始)
}
// 对关键点排序并去重
sort(a + 1, a + cnt + 1);
cnt = unique(a + 1, a + cnt + 1) - a - 1; // 更新实际关键点数量
非保序离散化
unordered_map<int, int> S;
int get(int x)
{
if (S.count(x)==0) S[x] = ++n;
return S[x];
}
算法应用
| 题目来源 |
标题 |
难度 |
星级 |
考察算法 |
一句话思路总结 |
| AcWing |
802 区间和 |
简单 |
⭐⭐⭐ |
离散化 + 前缀和 |
将所有涉及的坐标(插入点和查询端点)收集后排序去重得到离散化映射,用 find(x) 二分查找离散化后的位置,在离散化数组上执行单点加和前缀和查询,回答区间和为 \(s[r] - s[l-1]\),时间复杂度 \(O((n+m) \log(n+m))\)。 |
| 洛谷 |
P1904 天际线 |
普及- |
|
|
|
| 洛谷 |
AT_abc371_d 1D Country |
普及 |
|
|
|
| 洛谷 |
P1884 Overplanting |
普及+ |
|
|
|
| AtCoder |
AT_awc0089_e Painting the Fence |
|
⭐⭐⭐⭐⭐ |
离散化 + 扫描线 + 差分 |
将区间端点离散化后,用扫描线维护当前覆盖每段的区间集合 \(S\),若 \(S\) 非空且区间编号连续跨度 \(\leq K\)(\(mx-mn+1 \leq K\)),则用差分数组记录该段可被覆盖的起始范围:\(diff[mn+1] += len\)、\(diff[\max(0,mn-(K-(mx-mn+1)))] -= len\),最后对差分数组求前缀和,\(n+diff[i]\) 即为从位置 \(i\) 开始忽略 \(K\) 个连续指令后的覆盖长度,取最大值,时间复杂度 \(O(M \log M)\)。 |
【倍增】
算法模板
算法应用
【构造】
算法模板
算法应用
【随机化】
算法模板
算法应用