acm笔记
排序
快速排序
-
给定你一个长度为n的整数数列。 请你使用快速排序对这个数列按照从小到大进行排序。
void Qsort(int L ,int R) { if(L>=R) return; int mid = p[(L+R)>>1],i=L-1,j =R+1; while(i<j) { while(p[++i]<mid); while(p[--j]>mid); if(i<j) swap( p[i], p[j]); } Qsort(L,j); Qsort(j+1,R); }
-
给定一个长度为n的整数数列,以及一个整数k,请用快速选择算法求出数列从小到大排序后的第k个数。
int Findkth(int L,int R,int k) { if(L>=R) return p[L]; int mid = p[(L+R)>>1]; int i= L - 1,j = R + 1; while(i<j) { while(p[++i]<mid); while(p[--j]>mid); if(i<j) swap(p[i],p[j]); } int s = j - L + 1; if(s>=k) return Findkth(L,j,k); else return Findkth(j+1,R,k-s); }
归并排序
-
void Mg(int L ,int R,int Rend) { int i=L ,j=R,Lend = R -1; int k = L; while(i<=Lend&&j<=Rend) { if(p[i]<=p[j]) tmp[k++] = p[i++]; else tmp[k++] = p[j++]; } while(i<=Lend) tmp[k++] = p[i++]; while(j<=Rend) tmp[k++] = p[j++]; for(int i=L;i<k;i++) p[i] = tmp[i]; } void Msort(int L,int R) { if(L<R) { int mid = (L+R)>>1; Msort(L,mid); Msort(mid+1,R); Mg(L,mid+1,R); } }
ll Mg(int L ,int R,int Rend) { ll num = 0 ; int i=L ,j=R,Lend = R -1; int k = L; while(i<=Lend&&j<=Rend) { if(p[i]<=p[j]) tmp[k++] = p[i++]; else { num += R - i ; tmp[k++] = p[j++]; } } while(i<=Lend) tmp[k++] = p[i++]; while(j<=Rend) tmp[k++] = p[j++]; for(int i=L;i<k;i++) p[i] = tmp[i]; return num; } ll Msort(int L,int R) { if(L<R) { int mid = (L+R)>>1; ll s1 = Msort(L,mid); ll s2 = Msort(mid+1,R); ll s3 = Mg(L,mid+1,R); return s1+s2+s3; } return 0; }
快速幂
-
非递归
ll qpow(ll a,ll p,ll k) { ll ans = 1; while(p) { if(p&1) ans = (ans * a)%k; p>>=1; a =(a*a)%k; } return ans%k; }
-
递归
ll qpow(ll a,ll p,ll k) { if(p==0) return 1%k; ll b = qpow(a,p>>1,k); if(p&1) return ((b*b)%k*a)%k; else return (b*b)%k; }
前缀和 差分
int n,m; // n 表示数量 m表示询问的次数
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>p[i];
for(int i=1;i<=n;i++)
p[i] = p[i-1] + p[i]; // p[i-1] 为1~i-1的和
while(m--)
{
int a,b;
cin>>a>>b;
cout<<p[b] - p[a-1]<<endl; // 输出 a~b的和
}
int n,m,q;
cin>>n>>m>>q; // n 表示行数 m表示列数 q表示询问的次数
for(int i=1;i<=n;i++)
for(int j =1;j<=m;j++)
cin>>p[i][j];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
p[i][j] = p[i][j-1] + p[i-1][j] - p[i-1][j-1] + p[i][j];
while(q--)
{
int x1,x2,y1,y2;
cin>>x1>>y1>>x2>>y2;
cout<<p[x2][y2] - p[x1-1][y2] - p[x2][y1-1] + p[x1-1][y1-1] <<endl;
}
#include<iostream>
#include<cstdio>
using namespace std;
const int N = 100010 ;
int p[N] ;
// 在 l~r加一个数c
void insert(int l,int r ,int c)
{
p[l]+=c;
p[r+1] -= c;
}
int main()
{
int n,m,num,a,b,c;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
cin>>num;
insert(i,i,num);
}
for(int i=1;i<=m;i++)
{
cin>>a>>b>>c;
insert(a,b,c);
}
for(int i=1;i<=n;i++)
{
// 从 差分变为 前缀和
p[i] += p[i-1];
printf("%d ",p[i]);
}
return 0;
}
#include<iostream>
#include<cstdio>
using namespace std;
const int N= 1010;
int p[N][N];
void insert(int x1,int y1 ,int x2 ,int y2,int c)
{
p[x1][y1] +=c;
p[x2+1][y1] -=c;
p[x1][y2+1] -=c;
p[x2+1][y2+1] +=c;
}
int main()
{
int n,m,q,num;
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
scanf("%d",&num);
insert(i,j,i,j,num);
}
int a,b,c,d,f;
while(q--)
{
scanf("%d%d%d%d%d",&a,&b,&c,&d,&f);
insert(a,b,c,d,f);
}
// 变成前缀和
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
p[i][j] = p[i][j] + p[i-1][j] +p[i][j-1] -p[i-1][j-1];
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
printf("%d ",p[i][j]);
printf("\n");
}
return 0;
}
二分
二分的条件问题具有
-
二段性
-
单调性
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Enp1p0is-1616208468787)(C:\Users\james\AppData\Roaming\Typora\typora-user-images\image-20210319075850437.png)]
答案在黄色的右端点上
if M 是红色
L = M + 1
else
R = M
具体代码
while(L<R)
{
M = (L+R)/2;
if M 是红色
L = M + 1
else
R = M
}
return L
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rx8lvyQX-1616208468792)(C:\Users\james\AppData\Roaming\Typora\typora-user-images\image-20210319080345929.png)]
答案在红色的左端点上
if M 是红色
L = M
else
R = M - 1
具体代码
while(L<R)
{
M = (L+R+1)/2; // 注意这里要加 1 !!!
// 因为 M = (L+R)/2 是向下取整 如果 L = R - 1 ,M = R - 1 ,L = M = R - 1 死循环
// 所以要向上取整
if M 是红色
L = M
else
R = M - 1
}
return L
浮点数二分算法
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;
}
-
对于每个查询,返回一个元素 k 的起始位置和终止位置(位置从 0 开始计数)。 如果数组中不存在该元素,则返回 -1 -1
找到起始位置
int FindL(int k ) { int L = 0 , R = n-1 ; int mid; while(L<R) { mid = (L+R)>>1; if(p[mid]<k) L = mid + 1; else R = mid; } if(p[L]==k) return L; return -1; }
找到终止位置
int FindR(int k ) { int L = 0 , R = n-1 ; int mid; while(L<R) { mid = (L+R+1)>>1; if(p[mid]<=k) L = mid; else R = mid-1; } if(p[L]==k) return L; return -1; }
-
double L = -10000,R = 10000; while(R-L>=e) { mid=(L+R)/2; k = mid*mid*mid; if(k<n) L = mid; else if(k>n) R = mid; } printf("%.6lf",L);
区间的合并
给定 n 个区间 [l,r],要求合并所有有交集的区间
int s=p[0].l,e=p[0].r;
for(int i=1;i<n;i++)
{
if(p[i].l<=e)
e = max(e,p[i].r);
else
{
ans ++;
s = p[i].l;
e = p[i].r;
}
}
// 保存每一个区间
a[k].l=p[0].l;
a[k].r=p[0].r;
int i=0;
for(;i<n;i++)
{
if(p[i].l<=a[k].r)
{
a[k].r = max(a[k].r,p[i].r);
}
else
{
k++;
a[k].l=p[i].l;
a[k].r=p[i].r;
}
}
组合
从 1∼n 这 n 个整数中随机选取任意多个,输出所有可能的选择方案
for(int i=0;i<(1<<n);i++)
{
for(int j=0;j<n;j++)
if(i&(1<<j))
printf("%d ",j+1);
printf("\n");
}
从 1∼n 这 n 个整数中随机选出 m 个,输出所有可能的选择方案
for(int i=0;i<(1<<n);i++)
{
int j = i,num=0;
while(j)
{
if(j&1)
num++;
j>>=1;
}
if(num!=k)
continue;
for(int j=0;j<n;j++)
if(i&(1<<j))
printf("%d ",j+1);
printf("\n");
}
并查集
//初始化
for(int i=1;i<=n;i++)
f[i] = i;
// 查找
int Find(int k)
{
if(k!=f[k]) f[k] = Find(f[k]);
return f[k];
}