tricks
先警示一下
认真阅读题目(有的题目中包含部分解题信息)
注意预处理,离线之类。
简单的差分思想有时优化显著。
对于序列区间操作问题,尝试将其通过前缀和或差分转化为单点操作,对复杂度/推式子/再次优化有很大帮助。
异或哈希前缀和
如果不是这道题,甚至不知道这个
作用:判断区间内某个值是否出现偶数次。
原理: \(a\bigoplus a=0\)
求异或前缀和,若 \(sum_{l-1}=sum_r\) ,则 \(col_r\) 出现偶数次。
(利用随机数赋值实现哈希效果,用mt19937_64, rand 太弱)
mt19937_64 rnd(time(0))
线段树叶子结点非空子集数为 \(2^n-1\) 个
网络流:
拆点,建虚点,考虑inf的不同含义。
割表示选择或舍弃。
建图时借鉴之前思路,但不要局限于固有思维
判断质因数种类是否相同:
点击查看代码
for (int j = 1; j <= tot; ++j)
for (int i = pri[j]; i <= n; i += pri[j])
{
e[i].push_back(j);
++cnt[j];
}
之后map套vector判断
0/1矩阵,即邻接矩阵可转化成图论。
邻接矩阵的幂次表示路径的长度,即步数或跳数。
具体来说,一个图的邻接矩阵 \(A\) 的 \(k\) 次幂中的元素 \(a_{i,j}\) 表示顶点 \(i\) 和顶点 \(j\) 之间长度为 \(k\) 的路径的数量。
若原问题没思路可以先考虑其弱化版。
实数域上二分时加减eps (变化量不是1)
example
ldb l = 0, r = 10000000, ans = r;
while (r - l > eps)
{
ldb mid = (l + r) / 2.0;
if (check(mid))
r = mid - eps, ans = mid;
else
l = mid + eps;
}
trie 空间复杂度为 \(\sum len\)
线性阶乘逆元倒推\(O(n)\)求
example
inline void init()
{
jc[0] = inv[0] = 1;
for (int i = 1; i <= n; ++i)
jc[i] = jc[i - 1] * i % mod;
inv[n] = km(jc[n], mod - 2);
for (int i = n - 1; i; --i)
inv[i] = inv[i + 1] * (i + 1) % mod;
}
\(\vee\)为或,即 |,\(\land\)为与 ,即 &。
\(+\) : $a + b=a \vee b + a\land b $
\(\oplus\) : $a \oplus b=a \vee b - a\land b $
\(\therefore c+d=a + b , c-d=a \oplus b ,d \subseteq c\)
合法括号串:'('看作 1,')'看作 -1,前缀和为0且对于 \(l \le i \le r\) 都有 \(sum_r \le sum_i\) 即合法。
多个串的border相关问题:
建fail树,树上做(若最长公共border长为lca)
关于树的重心:
性质:\(dfn_i\)与 \(dfn_{(i+\frac{n}{2}-1)\%n+1}\) 一定不在同一连通块内(删重心后)
考虑该点是重心,则显然成立。
不是重心,则重心的祖先结点不超过\(\frac{n}{2}\)个,\(i+\frac{n}{2}\)一定不与 \(i\) 在同一连通块内
如果两条路径相交,那么一定有一条路径的LCA在另一条路径上
01序列排序:\(O(\log n)\)
线段树求区间和,区间推平
枚举二进制子集
点击查看代码
for(int j=(i-1)&i;j;j=(j-1)&i)//j是i二进制表示下的所有真子集的集合
cout<<j<<<' ';
对于string的赋值:
可以使用s[i]='*'的形式单点修改,也可写s+=t在后面连接。
特别注意,第一种形式要提前开够空间(它不会帮你动态分配空间)
如
#include <bits/stdc++.h>
using namespace std;
signed main()
{
string s;
s[0] = 'o';
cout << s << "\n";
return 0;
}
输出空串。
原因是空间不够。
应在赋值前开够手动空间。
改为
#include <bits/stdc++.h>
using namespace std;
signed main()
{
string s;
s.resize(10);
s[0] = 'o';
cout << s << "\n";
return 0;
}
即可正常输出。
若使用字符串拼接的方式修改(即s+=t)则无需担心空间。
之前好多次因为这个出错,所以以为string单修不对,今天终于揭秘了,原来是空间的问题。
好像之前写假过。
线段树空间应开到\(2^{\left \lceil \log_2n \right \rceil}\).
动态开点线段树建议开到\(2\left \lceil \log_2n \right \rceil\)倍大小。
这道题记录一下。
本来想直接整体二分,结果就是TLE+MLE。
考虑类似扫描线思想:
我们对时间轴扫描,在时间上依次加入或删除。
之后线段树上二分求前缀或和。
即可轻松解决(卡空间,线段树只能开2倍)
\(n\) 以内的素数为 \(\frac{n}{\ln(n)}\)级别,可优化复杂度。
推式子时有模,可考虑模的定义来拆模:
\(x \mod y=x-\lfloor\frac{x}{y}\rfloor y\)
有的式子中的位运算可拆位求,即拆为二进制每一位如何操作(找不到哪到题了)。
并查集维护序列段。
本文来自博客园,作者:HS_fu3,转载请注明原文链接:https://www.cnblogs.com/HS-fu3/p/19049576

浙公网安备 33010602011771号