题意: 给出一个01串,选一个长度至少为L的连续子串,使得串中数字的平均值最大. 分析: 能把这道题想到用数形结合,用斜率表示平均值,我觉得这个想法太“天马行空”了 首先预处理子串的前缀和sum,如果在坐标系中描出(i, sum[i])这些点的话. 所求的平均值就是两点间的斜率了,具体来说,在连续子串[a, b]中,有sum[b]-sum[a-1]个1,长度为b-a+1,所以平均值为(sum[b]-sum[a-1])/(b-a+1) 所以就把问题转化为:求两点横坐标之差至少为L-1,能得到的最大…
思路:枚举点t,寻找满足条件的点t': 计sum[i]为前i项合,平均值即为sum[t]-sum[t'-1]/t-t'+1 设(Pi=(i,Si),表示点在s中的位置,那么就可以画出坐标图,问题就转化为斜率最大: 于是画图分析. 几个点之间只有上凸下凸两种情况,取3个点为符合条件(t-t'>=L)的t',分析后得结论上凸点在各种情况(t)下都要舍去: 于是就可以不断更新,更新策略为新插入点,删除掉原来是下凸点,插入后变成上凸点的点: 随着t增大,t'只会增大(t增大,pt增大),所以增加到斜率变…
这道题写了两个多小时-- 首先讲一下怎么建模 我们的目的是让所有点的出度等于入度 那么我们可以把点分为两部分, 一部分出度大于入度, 一部分入度大于出度 那么显然, 按照书里的思路,将边方向后,就相当于从出度大于入度的运一个流量到 入度大于出度的点. 紫书 例题 11-13 UVa 10735(混合图的欧拉回路)(最大流) 所以我们可以把源点S到所有出度大于入度的点连一条弧, 弧的容量是出度-入度的一半 为什么容量是这样呢,等一下说 同理, 把所有入度大于出度的点和汇点T连一条弧, 弧的容量是入…
这道题用了数形结合, 真的牛逼, 完全想到不到还可以这么做 因为题目求的是平均值, 是总数除以个数, 这个时候就可以联系 到斜率, 也就是说转化为给你一堆点, 让你求两点之间的最大斜率 要做两个处理 (1)去掉上凸点, 因为上凸点是无论如何都不会为最优解的 (2)去掉之后每两个点之间的斜率是单调递增的, 这个时候要求切点. 切点即最大斜率, 所以就枚举终点, 然后找该终点对应的最大斜率 (也就是找到切点), 然后更新答案. #include<cstdio> #define REP(i, a,…
紫书上有很明显的笔误, 公式写错了.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…
这道题要逆向思维, 就是求出答案的一部分, 然后反过去去寻找答案存不存在. 其实很多其他题都用了这道题目的方法, 自己以前都没有发现, 这道题专门考这个方法.这个方法可以没有一直往下求, 可以省去很多时间.紫书里面把这叫做中途相遇法,双向广搜有点这个方法的味道.这里用到了二分查找, 总的时间复杂度是n的二次方乘logn #include<cstdio> #include<vector> #include<algorithm> #define REP(i, a, b) 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)这里时间很坑!我一开…
这里用到了一些数论知识 首先素因子都大于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]…
滑动窗口这个方法名字非常形象, 先是窗口的右指针尽量往右滑, 滑不动了就滑窗口的左指针, 滑到右指针又可以开始滑动为止. 这道题是要记录滑的过程中最大的窗口长度, 限制条件是窗口中不能出现重复的值. 重复的值有两种判断方法. 一种是set, 其实就是开个vis数组, 但是数据有10的六次方, 数组肯定开不下, 所以用set来代替, 用时间换空间, set的查询是logn, 比数组要慢. 第二种是用map计算出一个last数组, 保存的是与当前数相同的前一个数的坐标. 两种方法大同小异. set版…
首先分解,然后可以发现同一个因子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…
就是暴力枚举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…
这道题的建模真的非常的秀, 非常牛逼. 先讲建模过程.源点到每一行连一条弧, 容量为这一行的和减去列数, 然后每一列到汇点连一条弧, 容量为这一列 的和减去行数, 然后每一行和列之间连一条弧, 容量为19.然后求最大流, 最后矩阵中每一个元素的值就是其所在 列和行所连的弧的容量加1. 让我来解释一下.设每一个行为xi, 每一列为yi.在网络流中, 每一个xi都有多条弧连到所有的yi, 在矩形当中 每一行的每一个元素都是每一列的一部分.在网络流中,对于一个yi来说,  所有的xi都有一条弧连到这个…
设一个源点, 到所有设备连一条弧, 容量为1, 然后设一个汇点, 所有插座到汇点连弧, 容量为1, 然后 转换器也连一条弧, 容量为1. 最后最大流就是答案.其中注意节点数要开大一些. #include<cstdio> #include<queue> #include<iostream> #include<vector> #include<algorithm> #include<cstring> #define REP(i, a, b…
这道题就是给你一n长序列, 然后把这个序列按顺序分成很多段, 每段长s(最前面可以小于s, 只有第一段的后半段, 最后面也同样, 只有最后一段的前半段), 然后要求是每一段里面没有重复的数, 问你有几种分法 实际上看到连续s个数, 就可以想到滑动窗口, 可以提前初始化所给序列的每一段里面有没有重复的数, 然后再枚举第一段的终点, 然后一段一段去判断是否全部都没有重复的数字, 如果所有段都是的话, 那么就符合题目要求, ans++ 有两个地方要注意 (1)初始化的问题.这个思路有点像扫描法, 判断…
首先我们按照这三个U的位置来分类,当前三个U在i,i+1, i+2. 那么先看三个U前面,前面不能有三个U,因为我们不能重复计算 那么就是所有的组合减去有U的情况 为了叙述方便,我们设答案为f(n),没有三个U的方案数为 g(n) 那么显然g(n) = 2的n次方-f(n) 然后我们看三个U后面,后面就任意了是2的(n-i-2)次方 但是这里有一个问题,就是可能第i-1个可以和i,i+1组成三个U 或者i-2与i-1,i组成三个U 那么我们就强行让第i-1个是L,就可以避免这种情况 当i-1不存…
以9元组来代表当前状态,每一元是每一堆剩下的牌数 枚举当前状态所有可以拿掉牌的情况,然后递归下去求 概率,当牌拿完的时候概率为1 那么这里的实现非常的秀,用到了vector来代表9元组 然后还用到了map来实现记忆化搜索 #include<cstdio> #include<vector> #include<map> #define REP(i, a, b) for(int i = (a); i < (b); i++) using namespace std; ch…
用欧拉公式V-E+F=2 V是顶点数,E是边数,F是面数 具体推导见https://blog.csdn.net/QWsin/article/details/53635397 要用高精度 #include<cstdio> #include<algorithm> #include<iostream> #include<string> #include<cstring> #include<sstream> #define REP(i, a,…
这道题的题解有几个亮点 一个是每次只统计一个数字来简化思维 一个是统计当前位数的时候分三个部分来更新答案 具体看代码,有注释 #include<cstdio> #include<cstring> #include<algorithm> #define REP(i, a, b) for(int i = (a); i < (b); i++) using namespace std; const int MAXN = 10; int pow[MAXN], cnt[MAX…
从大到小安排杆子 分三种情况 (1)插到最左边,那么左边看到了杆子会多一个 (2)插到最右边,那么右边看到了杆子会多一个 (3)插到中间边,那么不影响左边和右边看到的杆子数 具体看代码 #include<cstdio> #define REP(i, a, b) for(int i = (a); i < (b); i++) using namespace std; const int MAXN = 21; long long d[MAXN][MAXN][MAXN]; int main()…
这道题方法非常的巧妙, 两层的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…
这道题想了很久不知道怎么设置状态,怎么拓展,怎么判重, 最后看了这哥们的博客 终于明白了. https://blog.csdn.net/u014800748/article/details/47400557 这道题的难点在于怎么设置联通的状态,以及怎么拓展判重 . (1)状态:这里状态先定义了一个格子cell, 有x和y坐标.然后set<cell>表示一个联通块, 再用set<set<cell>>表示n个连块可以组成的所有联通块, 这里是集合套集合. (2)拓展:每个格…
这道题发现一个性质就解决了 如果以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最大大…
设切割的区间为(j, i), 注意两边都是开区间. 然后可以预处理出以i为起点的最长连续递增的长度和以j为终点的最长连续递增的长度. 大致思路就是枚举i,右边这一侧的最优值就知道了, 然后这道题的关键就是就是j取哪里. (1)去掉干扰元素, 这一步非常的关键, 设题目给的数组为a, g(i)表示以i为结尾的最长递增序列长度 在j < i中, 如果 a[j'] <= a[j] 同时 g(j') > g(j), 那么 j这个元素肯定不是最优的.因为如果j可以取的话 j'就一定可以取, 而且更…
这道题目用扫描法 扫描法:在枚举的过程中维护一些重要的量, 从而简化计算 这道题用到了极角, 叉积, 高一的我表示一脸懵逼 不过自己去百度了一下好像大概看得懂. 这道题我还有一些疑问, 先这样吧 #include<cstdio> #include<cmath> #include<algorithm> #define REP(i, a, b) for(int i = (a); i < (b); i++) using namespace std; const int…
这道题感觉非常的秀 因为结果会很大,所以就质因数分解分开来算 非常的巧妙! #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…