Acm训练
Acm训练
吃饺子不蘸醋OxO
这个作者很懒,什么都没留下…
展开
-
各种dp的总结(未完)
区间DP对于区间dp 一般都有一套模板所有的区间dp问题,第一维都是枚举区间长度,一般 len = 1 用来初始化,枚举从 len = 2 开始,第二维枚举起点 i (终点右端点 j 自动获得,j = i + len - 1)for (int i = 1; i <= n; i++) dp[i][i] = 初始值for (int len = 2; len <= n; len++) //区间长度 for (int i = 1; i + len - 1 <=原创 2021-08-24 18:44:01 · 184 阅读 · 0 评论 -
[SDOI2009]HH的项链
传送门题目思路维护区间种类个数可太淦了 想了好长时间都没想出思路 后来突然看到题上是离线操作 所以想到可以动态维护线段树先把所有询问按右区间从小到大排列 然后从1到n遍历 每次都往当前位置加一个1 用last[a[i]]数组保存上一个a[i]在哪个位置 如果i等于0的话 说明前面还没有遇到过这个种类 如果不等于0 就把a[i]上个位置的1给删掉每次看一下当前位置是否和当前询问的右端点相同 相同的话就query一下得到当前ans代码#include <bits/stdc++.h>u原创 2021-08-23 12:24:40 · 114 阅读 · 0 评论 -
[NOIP2012]借教室
传送门题目思路刚开始是打算用线段树来维护的 后来感觉会超时应该用二分+差分二分的是第几次无法借出教室 通过差分来维护区间信息与每天的教室数量比较 如果多于某天天教室数量 则r=mid题目存在单调性 m越大 越容易不满足要求代码#include <bits/stdc++.h>using namespace std;const int N = 1e6 + 10;int n, m;struct Node{ int d, s, t;}order[N];int原创 2021-08-23 12:29:07 · 194 阅读 · 0 评论 -
AcWing 340. 通信线路
题目思路先把题目目的翻译一下 题目的目的就是想让我们找到一条第k+1长的最小边所以我们可以用二分来找那条边权 用双端队列来判断是否满足条件每次遍历 大于mid的边算一条边权为1的边 小于等于mid的边算边权为0的边这里二分还有一个注意事项假设有三条长度为mid的边 然后有k-2条大于mid的边 那么这时候最优解应该是mid 但是二分出的结果是f(u) < k 但是若check mid-1 的边 会得出f(u - 1) > k 所以我们让判断结果为 f(u) <= k如果满足原创 2021-08-17 23:57:47 · 111 阅读 · 0 评论 -
AcWing 341. 最优贸易
题目链接题目思路题目的要求是找出一条路径中的最大点权减去最小点权值最大我们可以spfa正向求一遍最小路 再反向求一遍最大路对于每个点的最小值dmin即为从起点到此点所经历的最小点权值对于每个点的最大值dmax即为从终点到此点所经历的最大点权值有一个注意的事项在函数部分中 如果你没设一个全局变量d而是函数变量 memset的时候要sizeof你所选的那个全局变量代码#include <iostream>#include <algorithm>#include &原创 2021-08-17 23:56:23 · 111 阅读 · 0 评论 -
Acwing 342. 道路与航线
题目大意有m1条道路 都为双向路 且边权大于0 有m2条航线 都为单向路 边权可能小于0 给出起点S 问从S到各点的最小距离为多少 若不存在则输出 “NO PATH”题目思路本来是想用spfa直接从起点搜最短路的 结果恶心的是这个题把spfa卡掉了 所以只能用dijkstra来搜了 但是dijkstra不能带有搜负权边的图 但是题中不难发现只有单向路即航线的边权可能为负权 而道路的都为正权边 所以我们可以先建立拓扑图 然后把道路输入后找连通块 每次dijkstra都在各自的连通块上进行 如果搜到了下一原创 2021-08-18 11:24:19 · 132 阅读 · 0 评论 -
Acwing 343. 排序
题目思路题目中的A>B 转化为 g[A][B] = 1 然后每次输入一次就进行一次floyd再check一下是否能确定有无解 对于每个i 都没有d[i][i] = 1 或者 d[i][j]=d[j][i]=1如果能确定 更新一下t 最后方便输出然后对于从小到大输出顺序可以遍历n次每次找最小的那个即无法找到d[i][j]=1的i值为当前最小值代码#include <iostream>#include <algorithm>#include <cstdio&g原创 2021-08-18 15:04:27 · 92 阅读 · 0 评论 -
P3147 [USACO16OPEN]262144 P
一个dp水题 但是刚开始却没有想出来还是看了题解题目链接题目思路刚开始想的是区间dp后来发现数据太大肯定会爆 发现可以这样做用 f[i][j] 表示区间合并结果为i的左端点为j的右端点值则有 f[i][j] = f[i - 1][f[i - 1][j]] 这个状态转移方程看数据 n= 262144 2的18次方 所以最多加十八 即最大范围只能到58了代码#include <bits/stdc++.h>using namespace std;const int N = 60原创 2021-10-04 14:52:53 · 101 阅读 · 0 评论 -
P3205 [HNOI2010]合唱队
真没想到会是区间dp 还是要多耍一耍对于区间dp的一些思考 一般什么题用得到区间dp呢?区间dp一般都是由小区间转移到大区间 即通过状态方程不断从小区间更新大区间 通过小区间的各种变量以及怎样去更新大区间 更新的值来确定状态方程怎么写题目思路通过观察题目可以得到 每次加人都是往原区间的左边或者右边去加 每次都是比较的本次需要加的人和上次加的人 则我们可以用 f[ i ][ j ][ 0 / 1 ]来表示当前方案 其中i为区间左端点 j为区间右端点 0表示是从左边加的人 1表示是从右边加的人 从而得到原创 2021-10-04 21:12:22 · 89 阅读 · 0 评论 -
P4302 [SCOI2003]字符串折叠
对于字符串的区间dp 寻找叠加后的最小值 其中叠加转化后的括号和数字均算为字符串长度唯一的难度就是去想到怎样判断字符串是否可以折叠我们可以只判断一个区间中整个字符串是否可以完全折叠为一个 其他的直接相加即可题目思路我们用f[i][j]来表示从i到j处理后的字符串最小长度 所以有如果f[i][j]可以完全叠加 则为min(f[i][j], 叠加后长度 + 2 + 个数)用一个check函数来判断字符串是否可以叠加void check(int l, int r, int len){ for (原创 2021-10-05 20:26:40 · 145 阅读 · 0 评论 -
P2470 [SCOI2007]压缩
一道不错的区间dp题 思考了好长时间题目链接题目思路我们令 f[i][j][k] 为由i到j且其中是否存在M的区间长度最小值可以分类讨论对于f[i][j][0] 即区间内不存在M的情况 我们可以想到 如果区间内前半段和后半段相等 我们是可以分解为 f[i][mid][0] + 1 即把后半段变为R所以对于每个长度的 f[i][j][0]都处理一次 求最小值 则f[i][j][0] = f[i][k] + j - k;再去考虑f[i][j][1]我们可以遍历区间内的每个点 往里面加M 则可以得原创 2021-10-05 22:50:33 · 99 阅读 · 0 评论 -
P5020 货币系统
一个背包思维题题目链接题目思路对于一个与[n, a]相同的货币系统 如果在a中有可以用其他值表示出来的值 我们就可以将它删去 将所有可以删掉的值删去后我们就可以得到最小的m [m, b]系统我们可以通过背包来解决这个问题ac代码#include <bits/stdc++.h>using namespace std;const int N = 25010;int n;int a[N], f[N];void solve(){ scanf("%d", &n);原创 2021-10-06 19:58:19 · 128 阅读 · 0 评论 -
CF730J Bottles
01背包问题题目链接题目思路先排序贪心找到需要的最少瓶子 再01背包求求所需瓶子内本来的水的最大值即可因为总水量是一定的 如果最后方案瓶子中原来的水越多 则从外面往里倒的水越少即所需时间越少我们可以用 f[m][k] 来表示 总容量为m 已经用了k个瓶子时的最大Vnomove则有 f[j][k] = max(f[j][k], f[j - b[i]][k - 1] + a[i])ac代码#include <bits/stdc++.h>using namespace std;原创 2021-10-07 12:05:40 · 142 阅读 · 0 评论 -
H - I love exam (HDU-6968)
这是一个典型的背包问题 让我们求学生Z在t天挂科数小于等于p的情况下借助总共给出的m份资料所得的最大分数总和我们用 f[i][j] 来表示第i科花费j时间所得的最大分数用 dp[i][j][k] 来表示前i门科目花费j时间挂科数为k的情况下所得的最大分数总和求f 和 dp都是01背包 只不过一个是科目得分 一个是 f 作为价值对于字符串我们可以用map转化为序号id 从而方便我们存数 再设一个vector来保存所有当前序号的价值和花费时间1. f[i][j] 的修改for (int i = 1;原创 2021-08-20 20:48:35 · 4708 阅读 · 0 评论 -
K - I love max and multiply (HDU - 6971)
题目大意给出 a 和 b 要求求一个c ck满足 ck = max(ai * bj} 且 k <= i & j 求c的总和思路很显然 i & j 只会生成小于等于i或j的数还是举例说明比较清楚 :比如k=10010 那么 i 和 j 可以选择 1xx1x的数字 所以我们可以从大到小遍历 维护所有1xx1x的最大值即 A[10010] = max{A[10010], A[10011], A[10110], A[11010]};为什么只求相对于10010变换一位0的数字的最大原创 2021-08-21 08:22:12 · 302 阅读 · 5 评论 -
Acwing 2476. 树套树 (线段树+平衡树)
题目链接用线段树和平衡树来维护区间信息 线段树每个区间中都设有一个平衡树的根对于所有操作1.先找到所有[l, r]的整数区间 然后在这个区间中看看有多少大于x的数 用splay可以完成2.二分求值 每次对于mid 找一下区间大于它的数有多少个 与k相比再缩短区间3.在所有包含w[pos]的区间中改数4 5. 对于所有[l, r]区间进行get操作找最小或最大的相对于x的数字代码#include <iostream>#include <algorithm>#incl原创 2021-09-18 21:04:08 · 168 阅读 · 0 评论 -
P3038 [USACO11DEC]Grass Planting G
题目链接思路模板题 难点在于怎样将点权变为边权我们可以考虑这样的事情 对于树上除了根之外的所有点 都有入边仅为1 出边可能为多条 则我们可以将这条入边的边权转化到这个点上树链剖分之后得到区间 我们可以知道对于树询问路径最后一段询问中 u v所在的重链为同一条 所以在转化后这段区间是连续的 我们只需要把这棵子树的根节点去掉即可 即(id[v] + 1, id[u])代码#include <bits/stdc++.h>#define L u << 1#define R原创 2021-09-28 15:21:34 · 201 阅读 · 0 评论 -
P4374 [USACO18OPEN]Disruption P
题目链接题目思路给两个点加额外的双向边时 可以观察到只有对于在树上这两个点之间的路径中出现断边的时候才有可能会用的到这条边 如下图所示 则我们可以维护每个边权 转化为点权 维护最小值 对于给出的query的边 我们可以通过分析这条边的两个端点的深度 哪个深度高 则断这条边的时候可以认为成断深度高的那个点 即输出当前点最小值我们可以将change的所有边从大到小排列 避免了pushup操作 直接覆盖即可代码#include <bits/stdc++.h>#define L u &l原创 2021-10-01 23:08:10 · 127 阅读 · 1 评论 -
Minimum Inversion Number (HDU - 1394)
题目大意给定一个排列数列 可以将其前面m个数移到后面得到一个新序列 问移动后所得的最小逆序对数量为多少思路线段树求逆序对经典题 每次输入w[i] 都将其放入线段树中 然后更新一下区间和sum 再query一下当前在w[i]输入前输入的数比w[i]大的数量为多少 即区间[w[i]+1, n]的当前sum为多少 最后加起来即初始序列逆序对 再从1到n遍历 每次求当前逆序对-(w[i] - 1)+n-w[i] 显然这是更新后的序列的逆序对数量 求最小值即可代码#include <iostream&原创 2021-08-19 11:16:17 · 116 阅读 · 0 评论