1.素数筛
//埃氏筛
bool is_prime[MAXN+1];
void init() {
for(int i = 2; i * i <= MAXN; i++) {
if(!is_prime[i]) {
for(int j = i*i; j <= MAXN; j += i) {
is_prime[j] = 1;
}
}
}
}
//欧拉筛
bool is_prime[MAXN+1];
vector<int> primes; // 存储素数
void init()
{
for (int i = 2; i <= MAXN; i++)
{
if (!is_prime[i])
primes.push_back(i);
for (int p : primes)
{
if (p * i > n)
break;
is_prime[p * i] = 1;
if (i % p == 0)
break;
}
}
}
2.二分查找
//模板1,可以寻找满足check()时的左边界
int bsearch_1(int l, int r)
{
while (l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
return l;
}
//模板2,可以寻找满足check()时的右边界
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;
}
lower_bound()函数
返回值是一个迭代器,指向的是有序数组第一个大于等于key的第一个值的位置
#include<algorithm>
//有序数组用法
int pos = lower_bound(a,a+len,key) - a;
//有序vector用法
int pos = lower_bound(a.begin(),a.end(),key) - a.begin();
3.并查集
#include<iostream>
#include<cstdio>
using namespace std;
int pre[1005];
void init(int n)
{
for(int i=0;i<=n;i++)
{
pre[i]=i;
}
}
int find(int x)
{
if(x==pre[x]) return x;
return pre[x]=find(pre[x]);
}
void join(int a,int b)
{
int t1=find(a);
int t2=find(b);
if(t1!=t2)
{
pre[t2]=t1;
}
}
int main()
{
int n,m;
while(~scanf("%d",&n))
{
if(n==0) break;
scanf("%d",&m);
init(n);
for(int i=0;i<m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
join(a,b);
}
int sum=0;
for(int i=1;i<=n;i++)
{
if(pre[i]==i)
{
sum++;
}
}
printf("%d\n",sum);//表示有多少个集合或者说分了多少组
}
return 0;
}
4.三分
求一个点到抛物线的最小距离
#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
double a,b,c,x,y;
double dist(double t)
{
double yy=a*t*t+b*t+c;
return 1.0*sqrt((x-t)*(x-t)+(yy-y)*(yy-y));
}
int main()
{
double lm,rm;
double l=-201.0,r=201.0;
scanf("%lf%lf%lf%lf%lf",&a,&b,&c,&x,&y);
while(fabs(l-r)>1e-9)
{
lm=1.0*(l+r)/2;
rm=1.0*(r+lm)/2;
if(dist(lm)>dist(rm))
{
l=lm;
}
else
{
r=rm;
}
}
printf("%.3lf",1.0*dist(l));
return 0;
}
5.快速幂与快速乘
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const mod = 1e9+7;
//快速幂
ll quickmi(ll a,ll b)
{
ll s=1;
while(b)
{
if(b&1) s=s*a%mod;
b>>=1;
a=a*a%mod;
}
return s%mod;
}
//快速乘
ll quickmul(ll a,ll b)
{
ll res = 0;
while(b)
{
if(b&1) res = (res + a) % mod;
b>>=1;
a = a + a % mod;
}
return res%mod;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
ll a,b;
cin>>a>>b;
printf("%lld\n",quickmi(a,b));
}
return 0;
}
6.矩阵快速幂
A为一个方阵,则Tr A表示A的迹(就是主对角线上各项的和),现要求Tr(A^k)%9973。
Input
数据的第一行是一个T,表示有T组数据。
每组数据的第一行有n(2 <= n <= 10)和k(2 <= k < 10^9)两个数据。接下来有n行,每行有n个数据,每个数据的范围是[0,9],表示方阵A的内容。
Output
对应每组数据,输出Tr(A^k)%9973。
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
struct mat{
ll a[11][11];
};
int n;
mat operator *(mat x,mat y) //重载*运算
{
mat ans;
memset(ans.a,0,sizeof(ans.a));
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int k=1;k<=n;k++){
ans.a[i][j]+=x.a[i][k]*y.a[k][j];
ans.a[i][j]%=9973;
}
}
}
return ans;
}
mat quickmi(mat p,int k)
{
mat b; // 单位矩阵,主对角线为1
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i==j)
{
b.a[i][j]=1;
}
else
b.a[i][j]=0;
}
}
while(k)
{
if(k&1)
b=b*p;
k>>=1;
p=p*p;
}
return b;
}
int main()
{
int T;
int k;
scanf("%d",&T);
while(T--)
{
ll sum=0;
struct mat p;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
scanf("%lld",&p.a[i][j]);
}
}
mat t=quickmi(p,k);
for(int i=1;i<=n;i++)
{
sum+=t.a[i][i]%9973;
}
printf("%lld\n",sum%9973);
}
return 0;
}
/*
input
2
2 2
1 0
0 1
3 99999999
1 2 3
4 5 6
7 8 9
output
2
2686
*/
7.最短路(Dijkstra单源最短路)
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int a[1005][1005];
int dis[1005];
int vis[1005];
int n,m;
void Dijkstra()
{
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)
{
dis[i]=a[1][i];
}
vis[1]=1;
for(int k=1;k<n;k++)
{
int mint = 999999;
int u;
for(int i=1;i<=n;i++)
{
if(!vis[i]&&dis[i]<mint)
{
mint=dis[i];
u=i;
}
}
vis[u]=1;
for(int i=1;i<=n;i++)
{
if(dis[i]>dis[u]+a[u][i])
{
dis[i]=dis[u]+a[u][i];
}
}
}
}
int main()
{
scanf("%d%d",&m,&n);
memset(a,999999,sizeof(a));
for(int i=1;i<=n;i++)
{
a[i][i]=0;
}
for(int j=0;j<m;j++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
a[u][v]=w;
if(a[v][u]<w)
{
a[u][v]=a[v][u];
}
else
{
a[v][u]=w;
}
}
Dijkstra();
printf("%d\n",dis[n]);
return 0;
}
最短路前提下最小花费
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
struct nide
{
int w,p;
}a[1005][1005];
int vis[1005];
int dis[1005];
int cos[1005];
int n,m;
void Dijkstra(int s)
{
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)
{
dis[i]=a[s][i].w;
cos[i]=a[s][i].p;
}
vis[s]=1;
for(int k=1;k<n;k++)
{
int mint=999999;
int u;
for(int i=1;i<=n;i++)
{
if(!vis[i]&&dis[i]<mint)
{
mint=dis[i];
u=i;
}
}
vis[u]=1;
for(int i=1;i<=n;i++)
{
if(dis[i]>dis[u]+a[u][i].w)
{
dis[i]=dis[u]+a[u][i].w;
cos[i]=cos[u]+a[u][i].p;
}
else if(dis[i]==(dis[u]+a[u][i].w))
{
if(cos[i]>cos[u]+a[u][i].p)
{
cos[i]=cos[u]+a[u][i].p;
}
}
}
}
}
int main()
{
int s,t;
while(scanf("%d%d",&n,&m))
{
if(!(n||m))
{
break;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
a[i][j].w=a[i][j].p=999999;
if(i==j)
{
a[i][j].w=a[i][j].p=0;
}
}
}
for(int i=0;i<m;i++)
{
int u,v,w,p;
scanf("%d%d%d%d",&u,&v,&w,&p);
a[u][v].w=w;
a[u][v].p=p;
if(a[v][u].w<w)
{
a[u][v].w=a[v][u].w;
a[u][v].p=a[v][u].p;
}
else
{
a[v][u].w=w;
a[v][u].p=p;
}
}
scanf("%d%d",&s,&t);
Dijkstra(s);
printf("%d %d\n",dis[t],cos[t]);
}
return 0;
}
8.拓扑排序
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
struct node
{
int v;//所指向的点
int w;//权值
/* node(){}
node(int vv,int ww)
{
v=vv;
w=ww;
}*/
};
vector<int> G[1005];
int deg[1005];
int a[1005];
int m,n;
void TPsort()
{
priority_queue<int, vector<int>, greater<int> > q;
for(int i=1;i<=m;i++)
{
if(!deg[i]) q.push(i);
}
int l=0;
while(!q.empty())
{
int t=q.top();
q.pop();
a[++l]=t;
for(int i=0;i<G[t].size();i++)
{
int tt=G[t][i];
if(deg[tt]>0)
{
deg[tt]--;
if(deg[tt]==0)
{
q.push(tt);
}
}
}
}
}
int main()
{
while(~scanf("%d%d",&m,&n))//m是边的条数
{
memset(deg,0,sizeof(deg));
for(int i=1;i<=m;i++) G[i].clear();
for(int i=1;i<=n;i++)
{
int u, v;
scanf("%d%d",&u,&v);
G[u].push_back(v);
deg[v]++;
}
TPsort();
for(int i=1;i<m;i++) printf("%d ",a[i]);
printf("%d\n",a[m]);
}
return 0;
}
9.sudoku
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int mm[11][11];
int row[11][11];
int col[11][11];
int grid[11][11];
bool dfs(int x,int y)
{
if(x==10)
return true;
bool flag=false;
if(mm[x][y])
{
if(y==9)
{
flag=dfs(x+1,1);
}
else
{
flag=dfs(x,y+1);
}
if(flag)
{
return true;
}
else
{
return false;
}
}
else
{
int k=3*((x-1)/3)+(y-1)/3+1;
for(int i=1;i<=9;i++)
{
if(!row[x][i]&&!col[y][i]&&!grid[k][i])
{
mm[x][y]=i;
row[x][i]=1;
col[y][i]=1;
grid[k][i]=1;
if(y==9)
{
flag=dfs(x+1,1);
}
else
{
flag=dfs(x,y+1);
}
if(!flag)
{
mm[x][y]=0;
row[x][i]=0;
col[y][i]=0;
grid[k][i]=0;
}
else
return true;
}
}
}
return false;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
memset(row,0,sizeof(row));
memset(col,0,sizeof(col));
memset(grid,0,sizeof(grid));
char mp[11][11];
for(int i=1;i<=9;i++)
{
for(int j=1;j<=9;j++)
{
cin>>mp[i][j];
mm[i][j]=mp[i][j]-'0';
if(mm[i][j])
{
int k=3*((i-1)/3)+(j-1)/3+1;
row[i][mm[i][j]]=1;
col[j][mm[i][j]]=1;
grid[k][mm[i][j]]=1;
}
}
}
dfs(1,1);
for(int i=1;i<=9;i++)
{
for(int j=1;j<=9;j++)
{
printf("%d",mm[i][j]);
}
printf("\n");
}
}
return 0;
}
10.差分数组
差分数组是什么?数组中后一项减前一项生成的数组就是差分数组。
什么问题可以用差分数组?多次修改数组区间的值(如果某个区间都加一个值,或者都减一个值,只需修改差分数组区间的端点值即可,ps:前闭后开)。
//定义diff为原数组nums的差分数组,那么两个数组的关系有:
diff[0] = nums[0]; // 当i=0
diff[i] = nums[i] - nums[i - 1]; // 当i > 0
//可以根据差分数组反过来推算出原有数组
nums[0] = diff[0]; // 当i=0
nums[i] = diff[i] + nums[i - 1]; // 当i > 0
//构建差分数组
void createDiff() {
diff[0] = nums[0];
for(int i = 1; i < nums.size(); i++) {
diff[i] = nums[i] - nums[i-1];
}
}
//给区间[i,j],增加val(可为负数)
void increment(int i, int j, int val) {
diff[i] += val;
if (j + 1 < diff.size()) {
diff[j + 1] -= val;
}
}
//还原数组(当然也可以直接在nums数组上操作,节省空间)
void getResult() {
res[0] = diff[0];
for(int i = 1; i < nums.size(); i++) {
res[i] = diff[i] + nums[i-1];
}
}
11.前缀和与后缀和
724. 寻找数组的中心下标 (可以用前缀和与后缀和方法,也可以先求出总和,然后在遍历数组时求左边的总和,总和-左边的总和就是右边的总和,看左右总和是否相等)
12.双指针法
第一种:快慢指针 283. 移动零
第二种:双端指针,向中间靠 941. 有效的山脉数组