本篇文章只是基础部分,分为基础算法和基础数论
基础算法
二分
整数二分
- 二分的本质并不是单调性,有单调可以用二分,用二分不一定得是单调;
- 二分的本质是条件和边界;
- 关于要不要加1,就看是l=mid还是r=mid;
bool check(int x) {/* ... */} // 检查x是否满足某种性质
// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)
{
while (l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid; // check()判断mid是否满足性质
else l = mid + 1;
}
return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l, int r)
{
while (l < r)
{
int mid = l + r + 1 >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
return l;
}
浮点数二分
注意
- eps的值要比题目中的小数位多2位
- 要注意边界和范围,比如0.01的三次方根
可以让l=-10000和r=10000
或者分正负两种情况,让一个的边界去max(1.0,n)或者max(-1.0,n)
bool check(double x) {/* ... */} // 检查x是否满足某种性质
double bsearch_3(double l, double r)
{
const double eps = 1e-6; // eps 表示精度,取决于题目对精度的要求
while (r - l > eps)
{
double mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid;
}
return l;
}
前缀和与差分
两者之间的关系: a[n] = b[1]+b[2]+b[3]+......b[n];
a[n]是前缀和;
b[n]是差分;
tip : 两个数组本没有时时联动关系,对某一数组操作以后需要通过手动操作同步
在题目中,前缀和 就是 根据a[n] 构造一个 其前n项和 a[n] ;
差分 是 构造一个b[n] 让a[n] 是他的前n项和 ;
前缀和
类似数列
S[i] = a[1] + a[2] + ... a[i];
a[l] + ... + a[r] = S[r] - S[l - 1];
//最最重要的是思想
for(int i=1;i<=n;i++)
S[i] = S[i-1]+a[i];
//每个i都有其对应的前缀和
差分
差分就一个思想 插入
给区间[l, r]中的每个数加上c:B[l] += c, B[r + 1] -= c
void insert(int l,int r,int c)
{
b[l]+=c;
b[r+1]-=c;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) insert(i,i,a[i]);
while (m -- )
{
int l,r,c;
scanf("%d%d%d",&l,&r,&c);
insert(l,r,c);
}
int sum=0;
for(int i=1;i<=n;i++)
{
sum+=b[i];
a[i]=sum;
//b[i]+=b[i-1];
}
for(int i=1;i<=n;i++) printf("%d ",a[i]);
return 0;
}
位运算
求n的第k位数字: n >> k & 1
返回n的最后一位1:lowbit(n) = n & -n
n的二进制表示中第k位是几(从个位算)
先把第k位移到最后一位 用右移 >>
看个位是几 n>>k & 1;
1的话是0000 0001
除了最后一位,前面全是0,所以可以看出来最后一位是什么
int main()
{
int n=10;
cout<<(n>>1&1);
return 0;
}
//输出是1,因为10的二进制是1010,右移一位后变成101;
//&是 同为1才为1
DFS
int dfs(int u)
{
st[u] = true; // st[u] 表示点u已经被遍历过
for (int i = h[u]; i != -1; i = ne[i])
{
int j = e[i];
if (!st[j]) dfs(j);
}
}
BFS
queue<int> q;
st[1] = true; // 表示1号点已经被遍历过
q.push(1);
while (q.size())
{
int t = q.front();
q.pop();
for (int i = h[t]; i != -1; i = ne[i])
{
int j = e[i];
if (!st[j])
{
st[j] = true; // 表示点j已经被遍历过
q.push(j);
}
}
}
最短路
基础数论
试除法判定质数
bool is_prime(int x)
{
if (x < 2) return false;
for (int i = 2; i <= x / i; i ++ )
if (x % i == 0)
return false;
return true;
}
质数筛
int primes[N], cnt; // primes[]存储所有素数
bool st[N]; // st[x]存储x是否被筛掉
void get_primes(int n)
{
for (int i = 2; i <= n; i ++ )
{
if (st[i]) continue;
primes[cnt ++ ] = i;
for (int j = i + i; j <= n; j += i)
st[j] = true;
}
}
质数筛(线性筛法)
int primes[N], cnt; // primes[]存储所有素数
bool st[N]; // st[x]存储x是否被筛掉
void get_primes(int n)
{
for (int i = 2; i <= n; i ++ )
{
if (!st[i]) primes[cnt ++ ] = i;
for (int j = 0; primes[j] <= n / i; j ++ )
{
st[primes[j] * i] = true;
if (i % primes[j] == 0) break;
}
}
}
最大公约数(欧几里得算法)
int gcd(int a, int b)
{
return b ? gcd(b, a % b) : a;
}
快速幂
求 m^k mod p,时间复杂度 O(logk)。
int qmi(int m, int k, int p)
{
int res = 1 % p, t = m;
while (k)
{
if (k&1) res = res * t % p;
t = t * t % p;
k >>= 1;
}
return res;
}