一些技巧和套路
套路
- 碰到一些两两之间的限制条件,可以考虑建图,很可能就是把一些约束条件转化成边,或者是跑网络流。 网络流建模好题1
- 如果碰到 转移状态很少 或者 情况可数 的题,可以试试看暴力打表或者手推,有概率能出来一些转移矩阵或者规律。
- 正着不行可以试试反的。比如从终点倒推。或是从起点出发情况很多,但是到终点的情况有限。
- 有时候要考虑能不能记忆化或者预处理。并不止搜索。比如你走到某个点之后后面的情况是可以预处理的,这种。
- 有限区间内出现个数这种有时候可以转化成线段覆盖的模型。for example
- 有状态但是不好推的时候可以考虑转化成状态转移图。比如
- 觉得很难处理?考虑一些特殊性质。例子
- 碰到什么“左右括号”“ \(01\)”“前/后缀中 \(A\) 不少于 \(B\) ”这种东西,可以考虑看成 \(01\) 然后做一些结论或者转化成折线。大选
- 一种奇怪的题意转化:01 数组,选择两个相邻的数并删除,在这个位置加入
x&y
或者x|y
,可以转化为选择两个相邻数并删除一个 BitOperation
图论
区间向区间连边,\((x+C,y+C)(1\leq C\leq num)\) ,考虑把 \(C\) 二进制拆分,然后维护 \(\log\) 个并查集,如果第 \(i\) 位是 1 那么就把第 \(i\) 个并查集里面的 \(x,y\) 合并。
如果是点向区间连边就是线段树优化建图。
对于一些需要枚举连边的题目,考虑从度数最小的点开始做能得到根号级别的优良复杂度。example
二分图匹配的一个或许有用的定理:ARC106E
Hall 定理(霍尔定理):
设二部图 \(G\) 中的两部分顶点集合分别为 \(X,Y\) (假设有 \(|X|\leq |Y|\) ) ,有完备匹配的充要条件是:
\(X\) 中任意 \(k\) 个点的点集与 \(Y\) 中至少 \(k\) 个点相邻。
推论:
令 \(N(W)\) 为 \(W\) 的所有邻居,那么二分图最大匹配数是 \(|X|-\max\{|W|-|N(W)|\}\) ,其中 \(W\) 是 \(X\) 的子集。
平面图最大流转最短路(对偶图):狼抓兔子
竞赛图的哈密顿路径应用:CF1514E
(题意:通过 \(O(n)\) 次询问获得一张竞赛图的点对连通状况。查询方式是获得一个点到一个点集的边中是否有出边)
解法:先类似归并排序的方式求出哈密顿路径,然后从后往前查询是否能够回溯,不能就不连通。
详解
找路径:考虑归并。比如找 \([l,r]\) 这些点的哈密顿路径,可以分为 \([l, mid]\) 和 \([mid+1,r]\) 求解。\([l,mid]\) 的哈密顿路径的起点是 \(u\) ,\([mid+1,r]\) 的是 \(v\) ,进行查询,如果是 \(u \to v\) ,那么就把 \(u\) 放在 \([l,r]\) 的哈密顿路径的第一个,否则把 \(v\) 放在第一个。
查询的时候从后往前枚举,维护一个指针 \(pos\) 表示 \(i\sim n\) 通过往前的边能够向前走到哪里(指 \(pos+1\sim i-1\) 都能走到)。如果 \(pos=i\) 了那么就把 \((1,i)\to(i+1,n)\) 的所有连通性都置为 \(0\) 即可。
相关结论:
- 对于一个竞赛图,一定有哈密顿通路
- 对于一个强连通竞赛图,一定有哈密顿回路
- 竞赛图缩点后肯定是一条链
网络流建模
- 拆点,按时间、进点出点……
- 最小割:最大权闭合子图、最大权独立集
- 可以参考这篇
数据结构
如果并查集直接合并会把两边信息混淆的话,可以考虑建虚点维护
数论
固定一端的序列 gcd 会有 \(\leq \log n\) 个连续段。gcd
字符串
unsigned long long
的 hash技巧
ull nxt_rnd(ull a){a^=a<<15;a^=a>>17;a^=a<<19;return a^1923510;}
scanf("%[^\n]", str)
正则用法,读到回车;%*s
可以空读字符串不存.
长度为 \(n\) 的字符串如果有长度为 \(x\) 的 border ,那么必然有长度为 \(n-x\) 的循环节(不一定完整)神仙的游戏 相关
位运算
有些状压直接转移复杂度太大的,可以考虑高八位和低八位分开算的奇妙trick。(CSP-S 2020初赛)
xza:这个非常常用,大家要掌握
可以试试按位算 or_and
多项式计数&生成函数
恰好
的题能够通过设一个恰好
和一个正好
然后反演得到。- 普通复杂度太大可以试试转化成下降幂的形式。
- 碰到 \(siz(c)=siz(a)+siz(b)\) 的题目,可以试试笛卡尔积
计算几何
结论:对于平面上两点 \(A,B\) 和一个圆 \(O\),直线 \(AB\) 与圆有交,当且仅当 \(A\) 的两切点连线和 \(B\) 的切点连线不相交。CF1446F
结论:设有同圆的两弦 \(A_1A_2,B_1B_2\) ,四个端点极角为 \(\alpha_1,\alpha_2,\beta_1,\beta_2\) ,两弦相交当且仅当区间 \([\alpha_1,\alpha_2]\) 和 \([\beta_1,\beta_2]\) 相交。(出处同上)
卡常
- 把判断
-1
换成~x
能快到飞起……
从兔队的博客里看到的加法取模:
b[i] -= Mod - x, b[i] += b[i] >> 31 & Mod;
大幅度空间优化其实是对时间的另类优化。
据说 memset
是比 fill
和 for
要快的。
fread
搞的 getchar
,在 歌唱王国 中一下子松了 32ms
……
const int L=1<<20;
char buf[L],*p1,*p2;
#define getchar() (p1==p2?(p2=buf+fread(p1=buf,1,L,stdin),p1==p2?EOF:*p1++):*p1++)
其他
快速根据日期算星期的公式(当然是要真实世界中的年月对应的)(格里高历之前的正确性不明)
ll CalcWeekDay( ll year,ll month,ll date )
{
if ( month==1ll || month==2ll ) month+=12,year--;
ll tmp=(date+month*2ll+(month+1)*3ll/5+year+year/4ll-year/100ll+year/400ll)%7ll;
return (ll)tmp+1;
}