好东西
- http://immortalco.blog.uoj.ac/blog/1974另类代码风格
- uoj的Custom Test是linux环境下的!Windows下的鬼畜错误可以去那里调试!
- 酷炫反演魔术 by vfk
- Matrix Tree定理相关 by lca
- 一些“基础(QAQ)”数据结构 拿来复习+刷题用
- 一些常用的数据结构维护手法 by zzq
- 标号的神奇应用
- Yves__学长的数学专栏
- 常用数据结构的功能及复杂度总结by czz
- Atcoder的数据
- 上下界网络流相关
- 斯特林数黑科技相关
- 多项式相关
- 网络流建图
注意事项
代码实现
- !!斜率优化dp,数论,数学题 开long long
- 变量名取得Chinglish一些
- !!!没开O2时慎用STL,尤其是set
- 珍爱生命,远离STL::queue,vector比queue快得多
- vector内存连续,开了O2尤其快,稠密图时写vector而不是模拟链表或邻接表
- priority_queue比set快得多
- 只要涉及乘法就应该警惕,考虑是否会爆int
- 数位dp,如果有很多维0/1,写成循环而不是每个0/1大讨论转移
- !!!动态点分治易错点:点分树上两点距离不能当成是边权相加
- 多项式各种操作中,做乘法扩大次数界的前后,记得将超出部分清0
- FFT空间开4倍
- !!!回溯(比如分治)如果要用到数组开static!如果要使用全局变量一定要谨慎,考虑是否已经被修改
- priority_queue是大根堆!
- 字符串哈希,字母不要对应成0!这样将无法区分ab和b
- 字符串哈希的进制数设成质数!(质数的幂在模意义下更不容易重复)
- 输出优化注意特判0
- 线段树合并不能最后再求答案,必须每个点与其子树合并完立即求答案(意味着询问离线),否则该点信息会被修改。如果一定要最后再求(意味着询问在线),可以可持久化。
- 变量名避免使用time,index;慎用x0,y0,next,last
- 用memcpy时如果A,B两个数组size不一致会鬼畜!
- 连通分量tarjan的时候记得要清空stack[top+1],否则退栈的时候比较会GG
- 斜率优化DP的时候在写不等式的时候如果写成乘法,一定要写成<=或>=,因为如果一个数是0,导致斜率函数为0,判断的时候一直相等决策就会一直停在那个地方,然后GG
- Splay的区间翻转操作,不仅要记得交换左右子树,有关左右区间的信息也要交换
- Splay区间翻转时,如果涉及左右区间的信息的维护,就不能打成下面这样:
if(tag[x])
{
swap(a[x][0],a[x][1]);//交换x的左右子树
swap(lx[x],rx[x]);//交换x的左右区间的信息,例如lx[x]表示x的区间从左端点往右的最大连续和
tag[a[x][0]]^=1,tag[a[x][1]]^=1;//下传标记
tag[x]=0;//标记清空
}
为什么这样会错?尽管我承认暂时lx,rx的值都是正确的。但是一旦执行update(x),lx[x],rx[x]就由lx[lson],rx[rson]来更新
对x进行了翻转时,仅交换了x的左右子树,并没有交换lx[lson],rx[rson]
所以更新出来的x点信息是错的
- Q:set怎么自定义优先级?
A:(慎用!!)
struct set_cmp{
bool operator ()(const int &x,const int &y){
return dfn[x]<dfn[y];
}
};
multiset<int,set_cmp> s[maxn];
- 调试出现鬼畜情况,大概率数组越界。
算法易错点
- 欧拉回路,走过的边要删除,否则若多次进入同一个点,每次扫一遍出边会TLE
策略
比赛策略
- if(当前状态=稳扎稳打) then (写对拍) else if(当前状态=放手一搏) then (手出小数据+肉眼)
- 能写线段树就不要写平衡树(树套树卡空间除外)
- DP:如果发现转移很有问题,又确信没有其他做法,尝试更换转移方式。
- 发现程序有bug,先不急着开始调,不管是WA,RE,TLE等,先检查一下是否是数组爆了,或是没开long long
- 对于空间不大的题,数组开大几倍
- 对于算法常数仔细考虑,不要轻易放过自以为会TLE的想法
- 任何一道题(即使是暴力),想好算法、实现方法再开打
- 调试的时候,如果为了调试修改了程序中的某一段,一定要打上注释,提醒自己调试完把程序变回去
- 比赛剩余1个半小时的时候必须开打,不能再拖
学习策略
- 多感性理解。
思路&套路
- 决策可以看成点,也可以看成直线。当一种难做时,思考另一种
- 图论(尤其网络流)常用套路:拆点,拆边
- Q:什么时候用(双向)bfs,什么时候用(ID)A*?A:当状态空间不大时,用bfs。当解的深度不大,能设计启发函数,或者感觉bfs铁定超时,就写IDA*
- 许多计数题可以考虑补集转化,比如要你求刚好为x,可以尝试求出>=x或<=x再减重
- 高斯消元的本质是找到向量 x ⃗ \vec x x满足 A × x ⃗ = c ⃗ A× \vec x=\vec c A×x=c,其中 A A A是系数矩阵, c ⃗ \vec c c是答案向量
- 求边的期望可以转化成求点的期望,再乘上走这条边的概率
- E ( x ) = ∑ i ≥ 0 P ( x = i ) ∗ i = ∑ i ≥ 0 P ( x ≥ i ) E(x)=\sum_{i\geq 0}P(x=i)*i=\sum_{i\geq 0}P(x\geq i) E(x)=∑i≥0P(x=i)∗i=∑i≥0P(x≥i)
- 如果贡献形如“所有方案的k次方和”,出现增量不太好维护。用第二类斯特林数把k次拆开变成组合数的形式,这样就变成了组合数的和,转移本质上是范德尔蒙恒等式(GDOI2018D2T2)
- 容斥、反演、变换是一家
- 插值大法好
- 差分大法好
- 组合意义大法好
- 当数据范围不像线性/一个log复杂度,什么思路都没有,考虑网络流
黑科技
- 可删除堆,思想是不对堆实时更新,而是在询问的时候检验是否被打上删除标记。
struct Heap {
priority_queue <int, vector<int>, greater<int> > Heap, Delt;
void push(int x) {Heap.push(x); }
void delt(int x) {Delt.push(x); }
int query() {
while (!Delt.empty() && Heap.top() == Delt.top()) {
Heap.pop();
Delt.pop();
}
if (Heap.empty()) return MAXV;
else return Heap.top();
}
};
- Long Long相乘取模
LL mult( LL A, LL B, LL Mo )
{
LL temp = ( ( LL ) ( ( db ) A*B/Mo+1e-6 ) * Mo );
return A*B - temp;
}
- 线性求1~n关于模mo的逆元
inv[i]=(mo-mo/i)*inv[mo%i]%mo;
- 两条路径在树上的交(from yangle71)
int depmax(int x, int y){return dep[x] > dep[y] ? x : y;}
int pathsection(int a, int b, int c, int d, int &e, int &f)
{
int u = lca(a, b), v = lca(c, d);
if(dep[u] > dep[v]) swap(a, c), swap(b, d), swap(u, v);
if(lca(u, v) != u) return 0;
e = depmax(lca(a, c), lca(a, d));
f = depmax(lca(b, c), lca(b, d));
if(max(dep[e], dep[f]) < dep[v]) return 0;
e = depmax(e, v), f = depmax(f, v);
return 1;
}
- 编译命令,Dev-c++放工具-编译选项-编译时加入以下命令