常用技巧&写法
Za
- 将满足某一性质的边长设为1,不满足的设为0,通过求最短路或者维护一个双端队列可以得到两点之间的满足该性质的边的最小值或最大值。
- 最大值最小/尽可能大/具有二段性的问题可以想想能否用二分。
- 具有结合律(1 + 1 + 1 + 1 == 1 + 3 == 2 + 2 -> 1 2 4 8)的性质,可以考虑倍增。
- 使用前缀和的时候,若用到-1,(j - i-1)考虑把所有的下标向后移一位
- 用Hash直接记不了pair 采用 LL 然后 x * 1e6 + y (看数据大小) 求这个hash值的时候1e6要转LL
- unsign int scanf("%u", &a), ull -> scanf("%llu", &b)
- 四舍五入, round(_double);
- 拓扑排序判断有无环: return tt == n - 1; [0, n - 1]
- 二维坐标化一维 x, y -> x * n + y(x, y [0, n - 1])
- 加法在mod 2 的意义上,相当于 ^ 运算(开始的值要mod 2后)
二分
- 小数点保留2位一般去4位(即比保留多两位即可)
- 实数二分[0, 1000], l 取不到 右端点, r 取不到 左端点, 虽然输出保留小数因为精度问题可能看不出来,还是要注意 是取不到的, 有时会有大问题.
卡常
#include <ctime>
int start = clock();
if(clock() - start >= CLOCKS_PER_SEC * 0.9)
{
cout << ... << endl;
exit(0);
}
注意
clock()函数很慢, 为了减少clock()的次数(控制在几百次可能),前面加上某个条件减少次数 如 i % 1000000 == 0
使用常量CLOCKS_PER_SEC
,因为不同系统下可能常量不同
离散化
若不是unordered_map,可能还是保序的快 qwq详见
- 保序 - 排序,判重,二分 (离线操作,需要把所有需要离散化处理的数据都读入后再操作)
- 不要求保序 - map, hash (不要忘记n = 0; S.clear();)(在线离散化)
sort(id + 1, id + n + 1);
n = unique(id + 1, id + n + 1) - (id + 1);
int get_id(int x)
{
return lower_bound(id + 1, id + n + 1, x) - (id + 1) + 1; // 返回下标以1开始
}
并查集
不要忘了压缩路径 fa[x] = t;
int get_fa(int x)
{
if(fa[x] == x) return fa[x];
int t = get_fa(fa[x]);
return fa[x] = t;
}
常常用一维数组,所以常把二维化为一维
数组模拟队列
int hh = 0, tt = -1 / 0 // 根据初始有无元素
while(hh <= tt)
// 加入
q[++ tt] = i;
// 弹出
int t = q[hh ++];
数组模拟循环队列
用到的是[0, N - 1];
int hh = 0, tt = 0;
while(hh != tt)
// 加入
q[tt ++ ] = i;
if (tt == N) tt = 0;
// 弹出
int t = q[hh ++ ];
if (hh == N) hh = 0;
高精度
先写非高精,再改为高精。
void add(LL a[], LL b[])
{
static LL c[M];
memset(c, 0, sizeof c);
for(int i = 0, t = 0; i < M; i ++)
{
t += a[i] + b[i];
c[i] = t % 10;
t /= 10;
}
memcpy(a, c, sizeof c);
}
void mul(LL a[], LL b)
{
static LL c[M];
memset(c, 0, sizeof c);
LL t = 0;
for(int i = 0; i < M; i ++)
{
t += a[i] * b;
c[i] = t % 10;
t /= 10;
}
memcpy(a, c, sizeof c);
}
int cmp(LL a[], LL b[])
{
for(int i = M - 1; i >= 0; i --)
if(a[i] > b[i]) return 1;
else if(a[i] < b[i]) return -1;
return 0;
}
void print(LL a[])
{
int k = M - 1;
while(k && !a[k]) k --;
while(k >= 0) printf("%lld", a[k --]);
puts("");
}
图论
- 符合拓扑序,不管边权正负。
- 一个的和/另一个的和 max -> 01分数规划 -> 二分一个定值 将不等式整理一下 重新定义点权或边权 再做一遍图论算法
- 正环(相当于最长路)spfa更新dist方式改变一下即可.
- 关于差分约束的问题(求最小解 最长路为例)
- 边权无限制,spfa - O(nm)
- 边权非负权(特殊),有向图tarjan,强连通分量 - O(n + m)
- 边权 > 0 拓扑排序 - O(n + m)
- 剪边若是左边的点集指向右边的点集,可以在两集合之间建一个虚拟点,边数n * m -> n + m,相当于找了个跳板 例,因使用拓扑排序涉及入度、出度时,要每次建立一个虚拟源点,可以采用n + i(i [1, m]),所以q[]大小 开N + M