【杂项】刷题日志
前言
感觉有时候自己做已经刷过的题目可能都不一定能刷出来,应该是对题目的理解还是不够深刻,我有想过每道题都写一篇博客,但感觉时间可能不太够,于是我就想对一些新题型和一些比较难的题目写一下博客,其余题目就大概讲一下算法与思路,就放在这篇博客里好了。
- 2023.5.25 佛系更新
关于内容
提供一些大概思路,与每天的做题。
还是先
比 2-sat 模板还要模板,应该不需要说什么了吧。
一开始的思路是
这道题跟P3199 [HNOI2009]最小圈几乎是一模一样吧,双倍经验,嘿嘿,就加个判无解的就行了。
P6086 【模板】Prufer 序列 :模板题,详见:Prufer 序列
李超线段树模板题,记得
P6033 [NOIP2004 提高组] 合并果子 加强版:
贪心思路想必大家都已经了解了,就是每一次选取最小的两堆果子进行合并,我们可以把这些需要插入的点用一个队列存储起来,首先这些需要插入的点肯定会越来越大, 这相当于延迟插入。当我们目标插入点就是我们当前最小的那一堆的时候,我们就把他插入进来,以上是思路,代码写出来大概就是,桶排,建立两个队列,排序结果放进第一个当中,合并结果放在第二个当中,每次选从两个队列队头选取比较小的合并。
P2971 [USACO10HOL]Cow Politics G:
题解
主要还是因为自己懒)。
今天来刷点CSP-S的真题,话说怎么之前都没去做,为什么现在才去做呢?
假设
这样,把每一个点都当成一条线段:
这就是一个贪心问题,按右端点排序后即可。
素数筛+贪心(哈哈,够简短吧)
CSP 打挂了,接下来一个月好好努力吧,争取 NOIP 取得个好名次。
水题,直接模拟,下标最好从 0~n-1
还是水题,开 map 映射,记得题目要看清,有坑点。
P2038 [NOIP2014 提高组] 无线网络发射器选址:
直接暴力枚举。
多重背包板子,记得先枚举数量,再枚举体积。
题解区里有bitset做法的,很有启发。
我们用一个bitset保存每个重量能否被称出即可。
#include <bitset> #include <cstdio> int a[10], w[10] = {1, 2, 3, 5, 10, 20}; std::bitset<1010> S; int main() { for(int i = 0; i < 6; i++) scanf("%d", a + i); S[0] = 1; for(int i = 0; i < 6; i++) for(int j = 0; j < a[i]; j++) S |= S << w[i]; printf("Total=%d\n", S.count() - 1); return 0; }
二项式定理,不要忘记还有 a和 b。
并查集,把
手推一下对于高一学生应该不是什么难事。
int 是去掉小数部分,一些细节需要注意,还是有点多的,
#include<bits/stdc++.h> #define eps 1e-5 using namespace std; int read(){ int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f; } double H,S1,V,L,K; int n; signed main(){ scanf("%lf%lf%lf%lf%lf%d",&H,&S1,&V,&L,&K,&n); double maxtime=sqrt(H/5); double mintime=sqrt((H-K)/5); int s=(int)(S1-mintime*V+L); int t=(int)(S1-maxtime*V); if(S1-maxtime*V<(double)t+eps) --t; t=min(max(-1,t),n-1),s=min(s,n-1); printf("%d\n",s-t); return 0; }
快速幂。
dfs,bfs也可以,这里提供 dp 思路
题目有点坑,看样例就明白了,是有向图,所以用
那如果是无向图呢,乱搞吧,反正数据范围这么小。
把每个同学看成一个点,信息的传递就是在他们之间连有向边,游戏轮数就是求最小环。
假如说信息由A传递给B,那么就连一条由A指向B的边,同时更新A的父节点,A到它的父节点的路径长也就是B到它的父节点的路径长+1。
这样我们就建立好了一个图,之后信息传递的所有环节都按照这些路径。游戏结束的轮数,也就是这个图里最小环的长度。
如果有两个点祖先节点相同,那么就可以构成一个环,长度为两个点到祖先节点长度之和+1。
这是一道模拟题,主要需要考虑以下几个约束条件:
1.每个工件的下一个工序必须在上一个工序之后
2.同一台机器同一时刻只能加工一个工件
3.按题目顺序安排下一个工件
按照题目说的做,不会错。
给定一个数列
1.它的最长不上升子序列长度;
2.最少能被划分成多少个不上升子序列。
Dilworth定理
对于一个偏序集,最少链划分等于最长反链长度。是不是很懵?在这道题中,我可以用人话复述一遍:
最长上升子序列的长度就是能构成的不上升序列的个数。
这样,我们经过简易的推理,就可以得出:
要使用导弹的次数就是最长上升子序列的长度。
如何求最长上升子序列呢?我们把求最长不上升子序列的代码稍微改动一下就好了。
这样,我们就可以在
这种题目考场时竟然没打出来,不行了。
现在居然还打炸,原来 5000 * 8000 * log2 8000 会T ,呜呜,只能
可以发现,对于一个已经有序的数列,单点修改一个值,我们可以通过前后冒泡各一次来保持有序,举个例子:
原序列为
我们可以从前往后冒泡,再次维持了数列的有序。这样的操作是
同样的,我们可以维护一个有序数列,并记录原下标与先下标之间的关系(用数组记录),每次修改后更新这种关系。
这样,修改操作是
算出每个价位对应的销量,解不等式,左右区间取绝对值最小的数。
建立两个双向链表,一个是对于每一个数的链表,另一个是对于每一块的链表,后者顺便维护每一个块的开始和结束,如果在初始数列的时候这个数等于上一个数,就合并到上一个数所在的块。
每一次操作,先枚举每一个块,删除第一个数,如果这个块删除之后就没了,就在块链表里面把这个块删了;如果删除后前后两个数可以合并,就合并这两个块。
每一个数只需要枚举两次,枚举复杂度稳定
新学会了 sscanf 与 sprintf 的用法。
可以用 sscanf 尝试读入形如 a.b.c.d:e 的字符串,并返回读取结果。
如果读到的数不是 5 个,肯定不合法。
然后再判断 a,b,c,d,e 的范围是否合法。
这个时候,再用 a.b.c.d:e 把这个字符串保存,判断是否跟原来字符串是否相同即可。
暴力就可以了,复杂度证明。
这道题的关键在于,sqrt(1)==1
也就是说,如果一个区间的最大值为1,我们就可以直接跳过这段区间的修改。只有最大值大于1时才有修改的必要。
而题中的数值大小范围在(0,1e12)之间。
每个数至多六次开平方便可得到1
每次暴力修改复杂度为
线段树维护即可。
其实也可以用树状数组维护前缀和,用并查集跳过
#include<bits/stdc++.h> #define N 1000100 using namespace std; #define int long long int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9') f=(ch=='-')?-1:1,ch=getchar(); while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x*f; } long long a[N],b[N]; int n,fa[N]; void add(int x,long long y) {while(x<=n) b[x]+=y,x+=x&-x;} long long ask(int x) {long long ans=0;while(x) ans+=b[x],x-=x&-x;return ans;} inline int find(int x) {return fa[x]==x?x:fa[x]=find(fa[x]);} signed main() { n=read(); for(int i=1; i<=n; i++) a[i]=read(),add(i,a[i]),fa[i]=(a[i]==1?i+1:i); fa[n+1]=n+1; int q=read(); while(q--) { int k=read(),l=read(),r=read(); if(l>r) swap(l,r); if(k) printf("%lld\n",ask(r)-ask(l-1)); else{ for(int i=find(l); i<=r; i=find(i+1)) { int x=sqrt(a[i]); add(i,x-a[i]),a[i]=x; if(x==1) fa[i]=find(i+1); } } } return 0; }
要想有解,易知需要满足下面三个条件:
(树肯定是有叶子节点的);
(分出一个大小为
这三个条件仅仅是必要条件吗?事实上是充要条件(满足这三个条件一定能构造出符合要求的树)。
下面给出一种构造方案:
我们设字符串中所有值为
。
我们先取一条长度
接下来,
易知这种方案一定能凑出
只需要将
tarjan 模板,调了半天,竟然是数组开小了。
一是有的罪犯既不能贿赂他也没有罪犯能揭发他,那么此题无解,我们在遍历时打上标记,然后从小到大枚举,只要遇见没有标记的就输出然后退出即可。
二是所有的罪犯都能直接或间接地被能贿赂的罪犯揭发。很明显,也有两种情况,一是没有环,那么资金就是贿赂那个没有入度的罪犯,二是有环,那么资金就是那个环里罪犯所需资金最小的。我们想,如果我们把环里的罪犯缩成一个点,那么全都是前者的情况了。
P5968 [POI2017]Reprezentacje ró?nicowe:
找规律,发现每两次会
三分模板,但好像二分不仅好写,效率还比三分高,二分 log2,三分 2* log3。
实际上二分,就是一个爬坡的过程——
发现前面大,就往前面爬;发现后面大,就往后面爬。爬到尖顶了,往刚才的方向一看:哎呀!要掉下去了!连忙回缩——无法回缩了,则当前即为最高点
二分
while(l+eps<r){ db mid=(l+r)/2.0; if(f(mid)<f(mid-eps)) r=mid; else ans=l=mid; }
三分
while(l+eps<r){ db len=(r-l)/3.0; db midl=l+len,midr=r-len; if(f(midl)>f(midr)) ans=midl,r=midr; else l=midl; }
简单分析一下就行,我们可以用一下分组思想,把每个颜色分为一组,再在每个颜色中按奇偶分组,所以一共有2m组。在乱搞一下就出来了。
期中考了,晚上就不来了。
切比雪夫距离转曼哈顿距离。
P5098 [USACO04OPEN]Cave Cows 3:
曼哈顿距离转切比雪夫距离。
重新学了一下标记永久化。
模板题。
还是模板题。
期中考炸了,估计班级倒数,两个班80多名,比上次月考退了50多名。
P4925 [1007]Scarlet的字符串不可能这么可爱:
水题,记得
前缀和优化 dp,应该还是比较好想的。(毕竟我都能场切的 dp,能有什么难的)
FJC 模板,记得局部变量每次要清空,会挂的很惨。
CF1732C2 Sheikh (Hard Version):
FJC模板,dp递推很好想,记得ans要清空。
还是FJC模板。
还是模板,你知道是什么吧,记得要开 __int128,不然 long long 会炸。
板题板题。
AT_code_festival_2017_quala_d Four Coloring
曼哈顿路径转哈密顿路径+构造
分段FJC,就行了。
有点难的,FJC优化状压dp,1<<32 可以用 unsigned int 自然溢出,或者用 __int128, long long 会炸。
反悔贪心,注意是环,且 m 个坑必须种满。
基本同上题,改为链,且 m 个坑不一定要种满,即小于 0 是 break。
P1344 [USACO4.4]追查坏牛奶Pollutant Control:
贪心,走路时间不能改变,先以距离排序,那就当时间超了的时候,把前面 AK 时间最久的场给去掉,不到了,可以用优先队列维护,记得如果距离要事先减的话,要倒序,自己想想为什么,要挂成 20 分。
单调性应该好想,但贪心应该比较难,可以把问题转化为,先拿出
P3545 [POI2012]HUR-Warehouse Store
:
基本同 小Z的AK计划 ,多了输出路径。
我们先尽可能满足所有我们遇到的顾客,并且使用一个大根堆存放所有你当前满足过的顾客的信息。当我们遇到一个我们无法直接满足的顾客,如果大根堆的堆顶的
小于当前的
,我们尝试取出大根堆的堆顶,将它删除并且将它的
加入你的存货数量中,然后你继续满足这一位顾客。
由于我们前提条件中有“大根堆堆顶的
大于当前的
”,如果我们不满足大根堆堆顶的顾客(即当前方案下所有我们满足的顾客中
最大的),我们一定是能够用取消堆顶的顾客所拿来的库存来满足当前顾客的,所以我们满足的顾客数量不降,并且你手头上的存货数量还有可能上升,也对答案有正面影响。所以不满足堆顶,满足当前项在这样的情况下是满足贪心的局部最优的。
ISAP ,dinic 竟然被卡了,呜呜。
update 11.18 : 发现这道题不卡 dinic,是自己的 dinic 打错了,呜呜。
Hash+权值线段树,枚举中间数
网络流,建图比较好想。
网络流,建图比较难想,最小割。
费用流,建图时一定要分清点,我调了好久,最小费用最大流开始时 vis[t]=1 而不是 vis[s]=1 。要开 long long 。
这道题真的恶心,竟然禁用的格子有重复,所以不能用 k,而要重新统计。二分图。
二分图,只要判断能匹配到就行了,不需要修改 match 。
二分图求最大匹配,建图可以用染色法。
网络流建图,转化为求最小割模型。
和上一道一模一样,双倍经验。
原来 FJC 不只有加和乘,还可以转化运算符,这样又是一道模板了。
网络流经典建图,考虑是输出方案,如果左部点的 d 不为 0 表示没有被割掉,是答案,右部点的 d 为 0 表示被割掉,也是答案,因为最小割保证图最后不连通。
发现有个 t ,不太好处理,那就加上时间维度,发现任意一条路径不会超过 10,于是我们将 10 秒内的状态都加到里面去,再套一个 FJC。
复习一下斜率优化。
对李超的理解又加深了。
斜率优化板题。
网络流的题目 N 和 M 的范围不要开太小,要算清楚,不然会 T。
NOIP了,就先不写。
P4402 [Cerc2007]robotic sort 机械排序
哎,还是本来都不想打博客了,但今天晚上被一道题给创死了,调了4个多小时,发现自己在调用 sz 时,没有发现 sz 已经发生那个变化了,上面那个是错误的,下面那个是正确的。
printf("%d ",sz[ch[root][0]]); int x=tree.find(i-1); int y=tree.find(sz[ch[root][0]]+2);
int ans=sz[ch[root][0]]; printf("%d ",ans); int x=tree.find(i-1); int y=tree.find(ans+2);
呜呜,4个多小时啊,好气啊,傻逼!!!!
鸽了这么久了,我终于回来了,NOIP虽然不是很理想,但好歹有一等,继续努力吧。
这次是真的。
对于一个要删去的元素,他产生的贡献是在它前面的权值比它大的,且删去时间比他晚的,在它后面的权值比它小的,且删去时间比他晚的,可以把时间也当成一个维度,变成三维偏序模板,用 cdq 就行了。
P2880 [USACO07JAN] Balanced Lineup G :
RMQ 模板,线段树,ST表均可,我这里为了复习一下 ST 表,就打了。
第一眼看成区间众数,其实没那么难,以为是单调不减的,相等的一定相连,所以转成求区间最值,在判断一下边界情况
二位区间最值,因为 n 的范围小,可以每行每行来做,又转成区间最值,用 ST 表维护即可。
注意
for(int i=2;i<=n;++i) lg2[i]=lg2[i-1]+(i&(i-1)?0:1); //是 i-1,不要手残打成 i
学了 tarjan 的离线算法
可以转化成从 n 个数中选出 2 个数 使得异或答案最大。
可以使用 01 trie + 贪心处理。
P3657 [USACO17FEB]Why Did the Cow Cross the Road II P :
树状数组优化近似LCS的dp。
P6119 [USACO17FEB]Why Did the Cow Cross the Road II G :
双倍经验。
吉司机线段树模板
这道题调了好久,才发现是 update 时, len 的长度搞错了,拿了一整段在那里搞,所以需要用长度时,最好事先记录好 l, r ,最后再减一减不容易出错。
需要注意的是,预处理的时候,如果有乘的数能整除
感觉现在就是凭心而写,想写就写,不想写就不写
将状态设计成点,跑 spfa 就行了,注意位运算的优先级,不确定时加个括号最好了。
差分约束板题,可以将条件转化成三角形不等式,跑 spfa,最后答案就是 dis数组,含有这数据也太水了吧,我 边数写成 n ,居然都有 90 分。
双倍经验,注意答案不能为负数。
二分 + dijkstra
好题,转换思路,与其设置边权使路径长度相等,不如设置路径长度去拟合边权的限制。
设
为
几乎同上题。
差分约束
差分约束,第九个点有点恶心,就是注意给 dis 赋值时不要覆盖,可能事先有 dis[b][a] = -1, 就不用赋值 dis[b][a] = 0。
本文作者:南风未起
本文链接:https://www.cnblogs.com/jiangchen4122/p/17433183.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步