2023.9 ~ 2024.1 总结

前言

本文不算知识总结,只记录一些本学期思维上提升的和对自己学习状态的总结(当然知识总结也是有的,但是我太菜了,还不全面)

1. 个人习惯反思

可跳过,主要写给自己

一个学期过去了,成长还是有的,但是还是两个老毛病:

  1. 浮躁,静不下心

  2. 心态不稳

听课

情景 1:

听课时想要记笔记,然后就跟不上了,索性就放弃了

属于毛病 1。

改进方法: 听完了课再记。

情景 2:

听不明白时直接放弃

属于毛病 1.

解决方案: 先记住解题思路,课后慢慢理解。

情景 3:

调一道题调很久调不出来,很容易着急,反而更做不出来。

属于毛病 2.

解决方案: 所有人都会有暂时做不出来的题,不要心慌,理清思路,重新整理想法才是最关键的。

做题

情景 4:

做着做着题容易开始水谷。

属于毛病 1.

解决方案: 做题时打开洛谷学术模式。

情景 5:

想一题想一会儿就去查资料或是问同学。

属于毛病 1.

解决方案: 想题时先理解题意,然后离开电脑或是去厕所想(在只有我自己的地方想)

情景 6:

明明有思路就是不想动

属于懒。

解决方案: 关掉除了 C++ 和题面的一切,强迫自己把手放在键盘上。

2. 技巧学习总结

骗分

  • 随机化(其实可能是正解)

本学期靠随机化的题目其实不少,让人惊掉下巴,不能想象随机这种不靠谱的不确定性算法也能成为题目正解,它靠着重复执行某一种正确性不高的随机算法,使其正确率提高,一般都可以达到 90% 或以上。让人大开眼界。比如 1.6 的 T3。

  • 暴搜

    本学期在一次次考试中,悟出了真理:

    学好暴搜 + 剪枝,也能 get 高分,甚至薄杀正解。

    其实暴搜的难点就在于你能否想到优秀的剪枝,减去不可行的节点,但是优秀的剪枝并不好想,它考验你的思维能力,技巧也只有其分类:

    • 可行性剪枝

    • 最优化剪枝

    • 重复性剪枝

    • 其他

    (太菜了,只知道这些)

    剪枝的形式很多变,没有固定的套路,一般是在你写裸暴搜时灵机一动想出来的或是找规律直接砍掉了一种类型的节点。总之,剪枝极其考验思维。

    然后它也可以用来辅助找到正解,如打表找规律,比空想要容易许多。

乱搞

此类题目多为找规律,抽象模型,简称人类智慧。

如其名,急需智慧,一般出出来没几个人能过。

所以,它和构造题一样,想不出来就只能跳过。

卡常

众所周知,在打代码时下意识地卡一点常有助于快速通过题目。

不加 -~ 万一 T 了怎么办? —— Frank(此人在 作业表 2 的 J 之后把所有的 ++ 换成了 -~)

  • mod

    众所周知,取模极慢无比。

    • 加法时,直接 mod 不要 %
    • 尽可能少 %
    • 只有在乘法时取一下即可
    #define Mod(x) x >= mod ? x - mod : x
    
  • 位运算

    位运算比加减乘除要快所以:

    • \2 >> 1
    • *2 << 1
    • x++ x=-~x
    • x -- x = ~-x
    • 特别的,对于线段树:ls = p << 1,rs = p << 1 | 1
  • 函数

    在写完封装函数版本之后把函数拆会主程序,减少函数调用时间。

    Eg.

    Now:

    inline void pushdown (int p) {
        if(t[p].tag2 != inf) {
            t[p * 2].tag2 = t[p].tag2, t[p * 2].tag1 = 0, t[p * 2].val = t[p].tag2;
            t[p * 2 + 1].tag2 = t[p].tag2, t[p * 2 + 1].tag1 = 0, t[p * 2 + 1].val = t[p].tag2;
        }
        t[p * 2].tag1 += t[p].tag1, t[p * 2].val += t[p].tag1;
        t[p * 2 + 1].tag1 += t[p].tag1, t[p * 2 + 1].val += t[p].tag1;
        t[p].tag1 = 0, t[p].tag2 = inf;
    }
    

    Before:

    void add (int p, int k) {
    t[p].tag1 += k, t[p].val += k;
    }
    void edit (int p, int k) {
        if(k == inf) return;
        t[p].tag2 = k, t[p].tag1 = 0, t[p].val = k;
    }
    void pushdown (int p) {
        edit(p * 2, t[p].tag2), edit(p * 2 + 1, t[p].tag2);
        add (p * 2, t[p].tag1), add (p * 2 + 1, t[p].tag1);
        t[p].tag1 = 0, t[p].tag2 = inf;
    }
    

    1.54s 1.2s

  • 快读快写

    一般能优化 [50,100]ms

  • 火车头

    迫不得已时使用。

DP

状态设计考思维,也是最底层的一步,相当于地基,当然我连地基都建不好

接着是决策,考对状态情况的考虑全不全,相当于连接柱。

确定边界条件相当于你要盖的层数,对于特殊情况可直接得到值的状态,从它开始,向后转移至其他状态。

转移方程,是一个状态转移到另一个状态的方法,相当于设计图。

最后实现就是施工队,把上述所有结合起来,一般要考虑转移顺序,以及最终答案取什么。(考验你码力)

个人的独特见解:

当你当前设的状态不能表示所有情况时,可以在状态中加一点信息,补全情况或方便转移。(前提是空间和时间允许)

一般题目中某个值的范围很小,就可以把其设入状态,可能成为推出转移方程的突破口。

DP 优化

从转移过程入手,现在我遇见过的优化方法有:

  • 前缀和优化
    行如:dpj=i=1j1dpi 的样子的转移方程可以用其优化,复杂度降一个级别。

  • 二进制分组优化(多重背包)
    对于每种物品,将其拆分为 log2k 种物品,每 2i 个物品捆绑成一个物品,然后 1k 中所有的数都可以用这些物品凑出来,相比每 i 个物品捆绑成一个物品,这种拆分方法更快,空间更小。

  • 单调队列优化
    当决策点的区间长度固定时,可以使用单调队列优化。

    行如: dpi=maxj=irildpj

    然后就把决策点入队,如果其编号 <ir 那么,该点失效,出队。

  • 树状数组优化
    最长上升子序列及其变种。

    由于要求 ai>aj 才能转移,考虑在值域上建树状数组,把每个 ai 作为下标,dpi 作为值,然后查询时 1ai1dpi 的最大值即为所求。

    由上例,发现树状数组优化是在对于转移取的是一个值域上 dp 的最大或最小值时使用。

  • 矩阵加速
    设计一个矩阵,利用矩阵乘法进行转移,随后可以用矩阵快速幂,将转移速度优化到 O(logn)

从状态设计入手,减少状态数量进而也能减少转移的时间。

从转移入手,减少转移时涉及的状态数,比如四边形不等式的决策单调性,减少转移涉及的状态。

But,我这部分几乎是盲区。

图论

我图论已经废了。 —— nikangle
俺也一样。—— qym

本学期学习的图论只有最短路。

其模板不难,但是可以在其上做很多花活,什么随机化,期望,虚点,补图……

废了,完全写不出来(学到了东西,但是写不出来,表述不出来)

随笔

后言

这个总结好像写了点什么,又好像什么也没写。

只能说未完待续吧……

后续更新

  • Updata in 2024.1.14 00:45:写完了框架了。

  • Updata in 2024.1.14 10:05 补充了 DP 部分

posted @   固态H2O  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示