这道题写了两个多小时-- 首先讲一下怎么建模 我们的目的是让所有点的出度等于入度 那么我们可以把点分为两部分, 一部分出度大于入度, 一部分入度大于出度 那么显然, 按照书里的思路,将边方向后,就相当于从出度大于入度的运一个流量到 入度大于出度的点. 紫书 例题 11-13 UVa 10735(混合图的欧拉回路)(最大流) 所以我们可以把源点S到所有出度大于入度的点连一条弧, 弧的容量是出度-入度的一半 为什么容量是这样呢,等一下说 同理, 把所有入度大于出度的点和汇点T连一条弧, 弧的容量是入…
这道题要逆向思维, 就是求出答案的一部分, 然后反过去去寻找答案存不存在. 其实很多其他题都用了这道题目的方法, 自己以前都没有发现, 这道题专门考这个方法.这个方法可以没有一直往下求, 可以省去很多时间.紫书里面把这叫做中途相遇法,双向广搜有点这个方法的味道.这里用到了二分查找, 总的时间复杂度是n的二次方乘logn #include<cstdio> #include<vector> #include<algorithm> #define REP(i, a, b) f…
紫书上有很明显的笔误, 公式写错了.g(k, i)的那个公式应该加上c(k-1)而不是c(k).如果加上c(k-1)那就是这一次 所有的红气球的数目, 肯定大于最下面i行的红气球数 我用的是f的公式, 我觉得这个稍微比f好理解一点.f(k, i) 表示k小时之后最上面i行红气球的个数. 分两种情况 如图所示 左上角的正方形的上面i行的红气球个数和前一个小时(也就是k-1)的整个正方形的上面i行的红气球个数是一样的, 因为右上角还有一个, 所以要乘2, 也就是f(k - 1, i) = 2 * f…
 这道题目可以把问题分解, 因为x坐标和y坐标的答案之间没有联系, 所以可以单独求两个坐标的答案 我一开始想的是按照左区间从小到大, 相同的时候从右区间从小到大排序, 然后WA 去uDebug找了数据, 发现这组数据过不了 3 1 1 3 3 1 1 3 3 2 2 2 2  正确输出是 1 1 3 3 1 1 2 2  我输出 IMPOSSIBLE 我发现当有包含关系的时候, 会先处理大区间而把小区间应该放的点覆盖掉了.所以我这个方法是不行滴, 然后就暂时不知道怎么改了.  之后我去看了他人的…
这道题用构造法, 就是自己依据题目想出一种可以得到解的方法, 没有什么规律可言, 只能根据题目本身来思考. 这道题的构造法比较复杂, 不知道刘汝佳是怎么想出来的, 我想的话肯定想不到. 具体思路紫书上讲得非常清楚了, 就不讲了.代码有详细注释 #include<cstdio> #include<vector> #define REP(i, a, b) for(int i = (a); i < (b); i++) using namespace std; const int M…
总的来说就是价值为1,时间因物品而变,同时注意要刚好取到的01背包 (1)时间方面.按照题意,每首歌的时间最多为t + w - 1,这里要注意. 同时记得最后要加入时间为678的一首歌曲 (2)这里因为要输出时间,也就是重量,那么这个时候初始化就要注意了. 因为如果只是输出价值的话就全部初始化为0,但是要输出重量,那就意味着 当前这个时间是恰好由几首歌组合,那么初始化的时候就要注意全部初始化为 -1,f[0] = 0,同时判断条件要f[j-w] != -1,这里要注意 (3)这里时间很坑!我一开…
设一个源点, 到所有设备连一条弧, 容量为1, 然后设一个汇点, 所有插座到汇点连弧, 容量为1, 然后 转换器也连一条弧, 容量为1. 最后最大流就是答案.其中注意节点数要开大一些. #include<cstdio> #include<queue> #include<iostream> #include<vector> #include<algorithm> #include<cstring> #define REP(i, a, b…
这道题的建模真的非常的秀, 非常牛逼. 先讲建模过程.源点到每一行连一条弧, 容量为这一行的和减去列数, 然后每一列到汇点连一条弧, 容量为这一列 的和减去行数, 然后每一行和列之间连一条弧, 容量为19.然后求最大流, 最后矩阵中每一个元素的值就是其所在 列和行所连的弧的容量加1. 让我来解释一下.设每一个行为xi, 每一列为yi.在网络流中, 每一个xi都有多条弧连到所有的yi, 在矩形当中 每一行的每一个元素都是每一列的一部分.在网络流中,对于一个yi来说,  所有的xi都有一条弧连到这个…
这里用到了一些数论知识 首先素因子都大于M等价与M! 互质 然后又因为当k与M!互质且k>M!时当且仅当k mod M! 与M!互质(欧几里得算法的原理) 又因为N>=M, 所以N!为M!的倍数 所以只要求1到M!中与M!互质的数的个数,在乘上N!/M! 可以理解为每一块M!有这么多,然而N!中有很多块M!,所以乘上N!/M! 然后根据phifac[n] = phi[n!] = n!(1-1/p1)(1-1/p2)......(1-1/k)的定义可以得出 当n为质数的时候 phifac[n]…
这道题想了很久不知道怎么设置状态,怎么拓展,怎么判重, 最后看了这哥们的博客 终于明白了. https://blog.csdn.net/u014800748/article/details/47400557 这道题的难点在于怎么设置联通的状态,以及怎么拓展判重 . (1)状态:这里状态先定义了一个格子cell, 有x和y坐标.然后set<cell>表示一个联通块, 再用set<set<cell>>表示n个连块可以组成的所有联通块, 这里是集合套集合. (2)拓展:每个格…
就是暴力枚举a, b然后和题目给的数据比较就ok了. 刘汝佳这道题的讲解有点迷,书上讲有x1和a可以算出x2, 但是很明显x2 = (a * x1 +b) 没有b怎么算x2?然后我就思考了很久,最后去看他的代码发现他的代码和他讲的是两回事 他的代码里直接是枚举a和b,不是按照书上的思路来的. 有点迷 #include<iostream> #define REP(i, a, b) for(int i = (a); i < (b); i++) using namespace std; con…
这道题要分隔草和洞, 然后刘汝佳就想到了"割"(不知道他怎么想的, 反正我没想到) 然后就按照这个思路走, 网络流建模然后求最大流最小割. 分成两部分, S和草连, 洞和T连 外围的草和S连一条无穷大的弧, 表示不能割, 若原来是洞就改成草然后加上花费. 然后非外围的草和S连一条容量为把草变成洞花费的弧, T同理. 然后相邻的格子之间连容量为围栏的弧. 最后是要把草和洞隔开, 所以求最小割就好了. ps:这个建模好牛逼-- #include<cstdio> #include…
二分图网络流做法 (1)最大基数匹配.源点到每一个X节点连一条容量为1的弧, 每一个Y节点连一条容量为1的弧, 然后每条有向 边连一条弧, 容量为1, 然后跑一遍最大流即可, 最大流即是最大匹配对数 (2)最小(大)权完美匹配(每个点都被匹配到).和最大基数匹配类似, 只是有向边的权值就是费用, 其余弧费用为0. 跑一遍最小费用流.最后要判断从s出发的弧是否满载, 不是则不能完美匹配.如果求最大权那么费用设为负的就ok. 这道题目每一个点恰好在一个圈内, 也就是说每一个点只有唯一的后继.反过来,…
这道题方法非常的巧妙, 两层的n*n, 第一层第I行全是第I个国家, 第二层的第j列全是第j个国家.这样能符合题目的条件.比如说第1个国家, 在第一层的第一行全是A, 然后在第二层的第一行就有ABCDE--这样A就和所有的国家都连接了,其他国家也是一样的.只能说这种方法非常巧妙吧,答案讲出来很简单,但是不容易想到. #include<cstdio> #define REP(i, a, b) for(int i = (a); i < (b); i++) using namespace st…
#include<cstdio> #include<iostream> #include<sstream> #include<algorithm> #define REP(i, a, b) for(int i = (a); i < (b); i++) using namespace std; const int MAXN = 50; int a[MAXN], n; void filp(int pos) //学习这里翻转的写法 { REP(i, 0, p…
这道题发现一个性质就解决了 如果以i为起点, 然后一直加油耗油, 到p这个地方要去p+1的时候没油了, 那么i, i+1, --一直到p, 如果以这些点 为起点, 肯定也走不完. 为什么呢? 用反证法, 假设以q(i  < q <= p)这个点为起点可以走完的话, 那么i这个点也一定可以走完 首先, i是可以达到q的, 因为i可以达到p, 而q是在p前面的, 而且从i开始走到q这个点剩下的油量肯定大于等于0, 而如果单纯从q开始走的话, 油量会等于0, 也就是说从i过来所有的油量反而会更多,…
解法和合并果子是一样的, 每次取最小的两个, 更新答案, 加入队列 #include<cstdio> #include<queue> #define REP(i, a, b) for(int i = (a); i < (b); i++) using namespace std; int main() { int n, x; while(~scanf("%d", &n) && n) { priority_queue<int, v…
这道题让最大值最小, 显然是二分答案 当题目求的是最大值最小, 最小值最大, 这个时候就要想到二分答案 为什么可以二分答案呢, 因为这个时候解是单调性的, 如果简单粗暴一点 就全部枚举一遍, 验证答案.但是因为答案满足单调性, 可以用二分的方法 来"枚举", 复杂度可以从n降到logn 开始我自己写了一个, 但是WA, 后来看了刘汝佳的代码, 发现要注意三点 (1)这道题的和的最大值会爆int, 要用long long. 养成看到题目的时候计算最大值看会不会爆int的习惯(int最大大…
这道题用了数形结合, 真的牛逼, 完全想到不到还可以这么做 因为题目求的是平均值, 是总数除以个数, 这个时候就可以联系 到斜率, 也就是说转化为给你一堆点, 让你求两点之间的最大斜率 要做两个处理 (1)去掉上凸点, 因为上凸点是无论如何都不会为最优解的 (2)去掉之后每两个点之间的斜率是单调递增的, 这个时候要求切点. 切点即最大斜率, 所以就枚举终点, 然后找该终点对应的最大斜率 (也就是找到切点), 然后更新答案. #include<cstdio> #define REP(i, a,…
设切割的区间为(j, i), 注意两边都是开区间. 然后可以预处理出以i为起点的最长连续递增的长度和以j为终点的最长连续递增的长度. 大致思路就是枚举i,右边这一侧的最优值就知道了, 然后这道题的关键就是就是j取哪里. (1)去掉干扰元素, 这一步非常的关键, 设题目给的数组为a, g(i)表示以i为结尾的最长递增序列长度 在j < i中, 如果 a[j'] <= a[j] 同时 g(j') > g(j), 那么 j这个元素肯定不是最优的.因为如果j可以取的话 j'就一定可以取, 而且更…
滑动窗口这个方法名字非常形象, 先是窗口的右指针尽量往右滑, 滑不动了就滑窗口的左指针, 滑到右指针又可以开始滑动为止. 这道题是要记录滑的过程中最大的窗口长度, 限制条件是窗口中不能出现重复的值. 重复的值有两种判断方法. 一种是set, 其实就是开个vis数组, 但是数据有10的六次方, 数组肯定开不下, 所以用set来代替, 用时间换空间, set的查询是logn, 比数组要慢. 第二种是用map计算出一个last数组, 保存的是与当前数相同的前一个数的坐标. 两种方法大同小异. set版…
这道题目用扫描法 扫描法:在枚举的过程中维护一些重要的量, 从而简化计算 这道题用到了极角, 叉积, 高一的我表示一脸懵逼 不过自己去百度了一下好像大概看得懂. 这道题我还有一些疑问, 先这样吧 #include<cstdio> #include<cmath> #include<algorithm> #define REP(i, a, b) for(int i = (a); i < (b); i++) using namespace std; const int…
首先分解,然后可以发现同一个因子ai不能存在于两个以上的数中 因为求的是最小公倍数,如果有的话就可以约掉 所以数字必然由ai的pi次方的乘积组成,那么显然,在 a最小为2,而b大于2的情况下a*b>a+b 所以要让和最小,就每一个ai的pi次方作为一个数就好了. 另外注意long long,素数和1 #include<cstdio> #include<cmath> #define REP(i, a, b) for(int i = (a); i < (b); i++) u…
这道题感觉非常的秀 因为结果会很大,所以就质因数分解分开来算 非常的巧妙! #include<cstdio> #include<vector> #include<cstring> #include<cmath> #define REP(i, a, b) for(int i = (a); i < (b); i++) using namespace std; const int MAXN = 11234; bool is_prime[MAXN]; vect…
(1)这道题要用到 unsigned long long, 弄了我好久 这道题范围可以达到2的64次方-1, 而long long 最多到2的63次方-1, 而unsigned long long可以到2的64次方-1,所以要用这个类型,注意这个类型只有正数 输入输出用printf貌似要用%llu, 我直接用cin cout方便一些 (2)这道题因为是去模的, 所以我们可以找规律, 可以发现斐波那契数列这样 一直取模是肯定会重复的,所以我们可以找到一个周期.所以我们就判断a的b次方 在这个周期中…
这道题写了好久-- 在三维空间里面有动的点, 然后求有几次最小生成树. 其实很容易发现, 在最小生成树切换的时候,在这个时候一定有两条边相等, 而且等一下更大的那条边在最小生成树中,等一下更小的边不在最小生成树中. 这样的话过了这个时刻,等一下更小的边就会代替等一下更大的边, 从而最小生成树切换 然后我们讨论怎么实现 第一步, 建边 因为这里边的长度是随时间变化的, 所以我们可以把其写成一个二次函数. 那么显然根据两点间距离公式, 长度的平方等于x方向距离的平方+y方向距离的平方+z方向距离的平…
这道题主要比较权值的时候要改变一下,其他地方基本一样. 比较权值的时候要考虑边的时间与a, b 可以设相对于当前边的时间now, 则now = d[u] % (a+b), 也就是当前这个边进行到整个a和b的循环 的哪个地方了.然后我们分类讨论. (1)当t >a的时候, 这种边在输入的时候就可以去掉了, 因为不管什么情况都走不了.打开的时间不足以 通过 (2)可以直接过的时候.这个时候now + t <= a, 也就是说当前的时刻加上过的时间仍然在打开的时间. 那么总时间就是d[u]+t, 就…
这道题要求每个节点只能经过一次,也就是结点容量为1, 要拆点, 拆成两个点, 中间连一条弧容量为1, 费用为0. 因为拆成两个点, 所以要经过原图中的这个节点就要经过拆成的这两个点, 又因为这两个点的 边的容量为1, 所以只能经过一次, 就等价于原图中的点只能经过一次. 拆点的时候要注意细节:起点和终点不用拆, 因为有两条路径, 所以起点和终点必须经过两次. 因此一开始的时候只拆2到n-1这些点, 拆成i与n+i.起点是1, 终点是n, 源点是0, 汇点是2 * n + 1 然后后来加边的时候,…
这道题用到了很多知识点, 是一道好题目.      第一用了状态压缩, 因为这里最多只有20位, 所以可以用二进制来储存状态 (要对数据范围敏感), 然后 涉及到了一些位运算.     第二这里是隐式图搜索, 和之前有一道bfs倒水的有点像, 就是题目和图论没有半毛钱关系, 但是却可以自己建 图来做, 把状态看作点, 把状态转移看作边.    第三因为求最短时间, 所以用了堆优化dijsktra. #include<cstdio> #include<queue> #define R…
这道题是Floyd的变形 改成d[i][j] = min(d[i][j], max(d[i][k], d[k][j]))就好了. #include<cstdio> #include<algorithm> #define REP(i, a, b) for(int i = (a); i < (b); i++) using namespace std; const int MAXN = 112; int d[MAXN][MAXN], n, m, q; int main() { in…