1.位运算
快速幂,快速乘
int power(int x,int b,int p){ //x^b mod p
int ans = 1%p;
while(b){
if(b&1){
ans = 1ll*x*ans%p;
}
a=1ll*a*a%p;
b>>=1;
}
return ans;
}
int mul(int a,int b,int p){//a*b%p
int ans = 0%p;
while(b){
if(b&1)ans=(ans+a)%p
b>>=1;
a=a+a;
a%=p;
}
return ans;
}
成对变换(常用于反向边)
若x为奇数 x xor 1 =x - 1
若x为偶数 x xor 1 =x+1
此玩意常用于反向边的变化(将正向边存n,反向边存n+1 (n&1==0))
lowbit
#define lowbit(x) (x&-x)
意为取出x二进制下最后一位(加个log2就是第几位)(2^k)mod 37互不相等(k∈[0,35])
前缀和与差分
一维前缀和
有
s u m i = s u m i − 1 + a i sum_i=sum_{i-1}+a_i sumi=sumi−1+ai
此时区间[l,r]的和为:
s u m r − s u m l − sum_r - sum_{l-} sumr−suml−
二维前缀和
s u m [ i ] [ j ] = s u m [ i − 1 ] [ j ] + s u m [ i ] [ j − 1 ] + a [ i ] [ j ] − s u m [ i − 1 ] [ j − 1 ] sum[i][j]=sum[i-1][j]+sum[i][j-1]+a[i][j]-sum[i-1][j-1] sum[i][j]=sum[i−1][j]+sum[i][j−1]+a[i][j]−sum[i−1][j−1]
那么对于
∑
i
n
∑
j
m
a
i
,
j
=
s
u
m
n
,
m
−
s
u
m
i
−
1
,
m
−
s
u
m
n
,
j
−
1
+
s
u
m
i
−
1
,
j
−
1
\sum_i^n \sum_j^m a_{i,j} = sum_{n,m}-sum_{i-1,m}-sum_{n,j-1}+sum_{i-1,j-1}
i∑nj∑mai,j=sumn,m−sumi−1,m−sumn,j−1+sumi−1,j−1
差分
对于数列
A
A
A
它的差分数列
B
B
B定义为:
B
i
=
A
i
−
A
i
−
1
B_i = A_i-A_{i-1}
Bi=Ai−Ai−1
此时
B
B
B的前缀和就是
A
A
A
对于A的区间加减操作可以变为对B的单点修改
例如:将 A l A_l Al至 A r A_r Ar全部加上 k k k
即为 B l + k , B r + 1 − k B_l+k,B_{r+1}-k Bl+k,Br+1−k
二分
整数二分
//在单调序列a中查找第一个>=x的
int find(int x){
int l=1,r=n;
while(l<r){
int mid = l+r>>1;
if(a[mid]>=x)r=mid;//若是严格大于就删掉等号
//这个判断可以换成check
else l=mid +1;
}
return a[l]
}
//查找第一个<=x的
int findself(int x){
int l=1,r=n;
while(l<r){
int mid = l+r+1 >>1;
if (a[mid]<=x)l=mid;
else r=mid-1;
}
return a[l];
}
实数二分
/*实数二分*/
//有两种方式
//方式1,设置精度(eps),以l+eps<r为条件。
//一般来说,需要保留k位小数时,eps取10^-(k+2)
double find(){
double l=s,r=t;//最大和最小
double eps=1;//10^0
for(int i=1;i<=k+2;i++) eps/=10;
while(l+eps<r){
double mid =l+r>>1;
if(check(mid))r=mid;
else l=mid;
}
return l;
}
// 方式2,在不容易确定精度时使用
//使用循环固定二分次数,次数要计算好,不要TLE,这种方法得到的精度一般比eps更高
double find1(){
double l=s,r=t;
for(int i=1;i<=100;i++){//二分一百次
double mid = l+r>>1;
if(check(mid)) r=mid;
else l=mid;
}
return l;
}
排序
离散化
代码附上:
void solve(){
sort(a+1,a+n+1);
for(int i=1;i<=n;i++){
if(i==1||a[i]!=a[i-1]){
b[++m]=a[i];
}
}
}
int query(int x){//查找x映射的谁
return lower_bound(b+1,b+m+1,x)-b;
}
中位数
性质1:在序列 A A A中,记A的中位数为 x x x,则 x x x使得
∑
i
=
1
n
∣
a
i
−
x
∣
\sum_{i=1}^n |a_i-x|
i=1∑n∣ai−x∣
最小
在有2n项的时候,x在排名为n~n+1之间都可以,
在有2n-1项时,x是排名为n的数
动态维护中位数:
对顶堆算法:
开一个大根堆和一个小根堆:
设有M个数,则将排名1~M/2的存入大根堆,M/2+1存入小根堆
如果有一个堆元素过多,就取出堆顶放入另一个堆,这样小根堆的堆顶就是中位数
第K大数
O
(
n
)
O(n)
O(n)算法
类似快排思想:
找一个基准数,把比它大的放在左边,比它小的放在右边。如果最终该数数所在 位置刚好是第k个,那么它就是答案。否则只需要在该数组的左半部分找,或者右半部分,此时k减去左半部分的数量。
逆序对
归并求逆序对
//归并排序及求逆序对
#include<bits/stdc++.h>
using namespace std;
#define N 1000005
int a[N] ,b[N];//b为辅助数组
long long cnt;
void merge_sort(int l , int r)
{
if(r-l > 0)//如果整个区间中元素个数大于1,则继续分割
{
int mid = (l+r) / 2 ;
int i = l; //辅助数组的下标
int p = l , q = mid+1;
merge_sort(l , mid);
merge_sort(mid+1 , r);
//printf("%d-%d %d-%d\n",p,mid ,q ,r);
while(p<=mid || q<=r)//左右两部分只要有一部分不为空
{
if(q>r || (p<=mid && a[p]<=a[q]))//从左半数组复制到辅助数组
b[i++] = a[p++];
else
{
b[i++] = a[q++];
cnt += mid -p +1;//将逆序对的个数累加起来
}
}
for(i = l ; i <= r; i++)//将b中排好序的元素复制到a中
a[i] = b[i];
}
}
int main()
{
int n;
while(cin >> n)
{
for(int i = 1 ; i <= n; i ++)
cin >> a[i];
cnt = 0;
merge_sort(1 , n);
for(int i = 1; i <= n; i++)
cout << a[i] << " ";
cout << endl;
cout << "逆序对有:" << cnt <<endl;
}
return 0;
倍增
基本思想
将可行答案成倍增长,得到一个 O ( l o g 2 N ) O(log_{2}N) O(log2N)的算法
代码长这样
int solve(){
int ans = 0,p=1;
while(p){
if(check(ans+p))ans+=p,p*=2;
else {
p/=2;
}
}
}
ST表
ST表是处理RMQ问题的利器,在
O
(
N
l
o
g
2
N
)
O(Nlog_2N)
O(Nlog2N)的时间预处理之后可以
O
(
1
)
O(1)
O(1)回答任意一个区间最值
具体思想
设 F i , j 表示子区间 [ i , i + 2 j − 1 ] 的最大值 F_{i,j}表示子区间[i,i+2^j-1]的最大值 Fi,j表示子区间[i,i+2j−1]的最大值
边界: ∀ F i , 0 = A i \forall F_{i,0}=A_i ∀Fi,0=Ai
有公式
F
i
,
j
=
m
a
x
(
F
i
,
j
−
1
,
F
i
+
2
j
,
j
−
1
)
F_{i,j}=max(F_{i,j-1},F_{i+2^j,j-1})
Fi,j=max(Fi,j−1,Fi+2j,j−1)
故代码为:
void ST(){
for(int i=1;i<=n;i++)f[i][0]=a[i];
int t = log(n)/log(2)+1;
for(int i=1;i<t;i++){
for(int j=1;j<=n-(1<<i)+1;j++){
f[j][i]=max(f[j][i-1],f[j+(1<<(i-1))][i-1]);
}
}
}
int ST_query(int l,int r){
int k= log(r-l+1)/log(2);
return max(f[l][k],f[r-(1<<k)+1][k]);
}