小技巧
我们要算一个点集中所有点到另一个点集中所有点的一些量的时候,可以建立一个超级源点和超级汇点,从多->多变成单->单
整体二分的时候,操作要可以撤销,才能保证复杂度,每一层到左边区间的过程中先撤销掉当前所有操作
整体二分的时候,不能当没有询问就返回,要把操作做完询问处理完才能返回
数形结合,题目中的式子像一个几何上的公式的时候,可以数形结合
对于某些涉及区间操作的问题的时候,可以考虑差分然后变成单点的问题
分块暴力,当所有修改对询问的影响都可以 \(O(1)\) 计算的时候,可以分块暴力艹过 \(1e5\) 的数据
考一棵树里面选了几个点,并且要有一个联通块包含这几个点的时候,我们可以考虑把点按照dfs序排序,然后联通块的最少边数就是 \(\frac {\sum dis(a_i,a_{i+1})} 2,(a_{n+1}=a_1)\)。
一种常见的dp状态是 \(dp[a][b][c][d]...\) 表示每种物品有几个,假如物品没有区别的时候,我们可以只考虑 \(a \le b \le c \dots\) 的状态,转移的时候也将数量排序再转移,可以大量减少状态数
reverse特别慢,尽量不reverse
char[] 求strlen特别慢,可以记录在变量里,不能反复求,尤其避免
for(int i=0;i<strlen(s);i++)
慢成狗。。。
线性基的小技巧:
我们要维护 \([l,r]\) 的线性基的时候,可以记录下来线性基的 \(i\) 位对应的序号,然后按照序号顺序更新线性基,像下面这样写:
int lb[40],at[40]; //线性基及编号 for(int i=1;i<=n;i++){ //枚举右端 int x=nw[i],p=i; for(int j=30;j>=0 && x;j--){ if(x&(1<<j)){ if(lb[j]==0){lb[j]=x;at[j]=i;break;} else if(at[j]<p){swap(at[j],p);swap(lb[j],x);} //把原来的向下更新 //因为如果两个同时拥有这一位,那么后来的肯定更优 x^=(1<<j); } } for(int x=head[i];x;x=nxt[x]){ //枚举右端点为i的查询,可以用邻接表存储 for(int j=30;j>=0;j--){ if(at[j]>=q[x].l) q[x].ans=min(q[x].ans,q[x]^lb[j]); } } }
Meet in the middle 极好的想法,当数据范围恰好卡着你两倍左右让你过不去的时候,极有可能是Meet in the middle
预处理!!!预处理!!!预处理!!!
好像数数题大部分都要预处理。。。
某一种dp要求你把所有的东西都消除,配对。。。之类的,可以考虑其中的某一个元素,这个元素一定会被消除,配对。。。然后可能能优化一下复杂度
size把 \(O(n^3)\) 的 \(\text{dp}\) 优化成 \(O(n^2)\)
一个数与一群数一个个取 \(\gcd\) 只会有 \(\log\) 种不同的答案,因为每次要么不变,要么变成一半或者更少,然后可以优化一些题
搜索的时候为了不重复可以只搜索单调不降的序列
当题目要求必须选某一个元素或者必须不选某一个元素的时候,可以用前缀加后缀合并得到答案
对时间分治的时候注意撤销操作要从后向前做!!!
合并果子的模型非常常见,当贪心想不出来的时候可以试试看