对象排序
class PII implements Comparable<PII>{int x;int y;public PII(int x,int y){this.x=x;this.y=y;}// 如果用到对象排序的话public int compareTo(PII o){return Integer.compare(x,o.x);//从小到大排序// return Integer compare(o.x,x); 从大到小排序}
}public class Main{static PII[] p=new PII[100010];public static void main(String[] args)throws IOException{BufferedReader in=new BufferedReader(new InputStreamReader(System.in));int n=Integer.parseInt(in.readLine());for(int i=0;i<n;i++){String[] strs=in.readLine().split(" ");int x=Integer.parseInt(strs[0]);int y=Integer.parseInt(strs[1]);p[i]=new PII(x,y);}Arrays.sort(p);}
}
归并排序
代码模板
void merge_sort(int q[],int l,int r){if(l>=r)return;int mid=l+r>>1;merge_sort(q,l,mid);merge_sort(q,mid+1,r);int k=0,i=l,j=mid+1;while(i<=mid && j<=r)if(q[i]<=q[j])tmp[k++]=q[i++]else tmp[k++]=q[j++];while(i<=mid) tmp[k++]=q[i++];while(j<=r) tmp[k++]=q[j++];for(i=l,j=0;i<=r;i++,j++)q[i]=tmp[j];
}
应用场景
- 求逆序对的数量
// 当j>i且q[i]>a[j]时 res+=mid-i+1;
整数二分
代码模板
bool check(int x){} //检查x是否满足某种性质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;
}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;
}
应用场景
- 对有序数列中快速查找某个满足条件的数
浮点数二分
代码模板
bool check(double x){} //检查x是否满足某种性质double bsearch_3(double l,double r){const double eps=1e-6;while(r-l>eps){double mid=(l+r)/2;if(check(mid))r=mid;else l=mid;}return l;
}
应用场景
- 从数据范围中找到某个浮点数,比如数的开三次方,开四次方
高精度
java版本高精度计算
import java.io.*;
import java.math.*;public class Main{public static void main(String[] args)throws IOException{BufferedReader in=new BufferedReader(new InputStreamReader(System.in));String str=in.readLine();BigInteger a=new BigInteger(str);str=in.readLine();BigInteger b=new BigInteger(str);// 高精度加法System.out.println(a.add(b));// 高精度减法System.out.println(a.subtract(b));// 高精度乘法System.out.println(a.multiply(b));// 高精度除法System.out.println(a.divide(b));// 其他常用方法// gcd最大公约数// max,min,mod,pow,abs,sqrt}
}
应用场景
看题目要求随机应变
一维前缀和
s[i]=s[i-1]+a[i]
s[r]-s[l-1]=a[l]+...+a[r]
一维差分
B[l]+=c;B[r+1]-=c;
// 然后对B数组求和
二维前缀和
s[i,j] = 第i行第j列格子左上部分所有元素的和
以(x1,y1)为左上角,(x2,y2)为右下角的子矩阵的和为:
s[x2,y2] - s[x2-1,y2] - s[x2,y1-1] + s[x1-1,y1-1]
二维差分
给以(x1,y1)为左上角,(x2,y2)为右下角的子矩阵中的所有元素加上c:
s[x1,y1] += c, s[x2+1,y1] -= c,s[x1,y2+1] -= c, s[x2+1,y2+1] += c
然后对s数组求和就行
应用场景
适用于在某个矩阵区间加上某个值后,求每个位置的值
位运算
求n的第K位数字: n>>k&1
返回n的最后一位1:lowbit(n) = n & -n
import java.io.*;public class Main{public static void main(String[] args)throws IOException{BufferedReader in=new BufferedReader(new InputStreamReader(System.in));int n=Integer.parseInt(in.readLine());System.out.println(lowbit(n));}static int lowbit(int x) {return x & -x;}
}
区间合并
import java.io.*;
import java.util.*;class PII implements Comparable<PII>{int l;int r;public PII(int l,int r) {this.l=l;this.r=r;}public int compareTo(PII o) {if(l>o.l)return 1;else if(l==o.l&&r>o.r)return 1;else return -1;}
}public class Main{static int N=100010;static PII[] p=new PII[N];static int n;public static void main(String[] args)throws IOException{BufferedReader in=new BufferedReader(new InputStreamReader(System.in));n=Integer.parseInt(in.readLine());String[] strs;for(int i=0;i<n;i++) {strs=in.readLine().split(" ");int l=Integer.parseInt(strs[0]);int r=Integer.parseInt(strs[1]);p[i]=new PII(l,r);}merge();}static void merge() {List<PII> res=new ArrayList<>();Arrays.sort(p,1,n);int st = (int)-2e9;int ed = (int)-2e9;for(int i=0;i<n;i++) {if(ed<p[i].l) {if(st!=-2e9)res.add(new PII(st,ed));st=p[i].l;ed=p[i].r;}else ed=Math.max(ed,p[i].r);}if(st!=-2e9)res.add(new PII(st,ed));System.out.println(res.size());}
}
单链表
// head存储链表头,e[]存储节点的值,ne[]存储节点的next指针,idx表示当前用到了哪个节点
int head,e[N],ne[N],idx;//初始化
void init(){head=-1;idx=0;
}//在链表头插入一个数a
void insert(){e[idx]=a,ne[idx]=head,head=idx++;
}//将头结点删除,需要保证头结点存在
void remove(){head=ne[head];
}
栈
// tt表示栈顶
int stk[N],tt=0;//向栈顶插入一个数
stk[++tt]=x;// 从栈顶弹出一个数
tt--;//栈顶的值
stk[tt];//判断栈是否为空,如果tt>0,则表示不为空
if(tt>0){}
队列
// hh表示对头,tt表示队尾
int q[N],hh=0,tt=-1;//向队尾插入一个数
q[++tt]=x;//从队头弹出一个数
hh++;//判断队列是否为空,如果hh<=tt,则表示不为空
if(hh<=tt){}
单调栈
// 常见模型:找出每个数左边离它最近的比它大/小的数
int tt=0;
for(int i=1;i<=n;i++){while(tt && check(stk[tt],i))tt--;// while(tt && a[stk[tt]]>=a[i])tt--;stk[++tt]=i;
}
单调队列
// 常见模型:找出滑动窗口的最大值/最小值
int hh=0,tt=-1;
for(int i=0;i<n;i++){while(hh<=tt&&check_out(q[hh]))hh++;// 判断队头是否滑出窗口while(hh<=tt&&check(q[tt],i))tt--;q[++tt]=i;
}
import java.io.*;public class Main{static int N=1000010;static int[] a=new int[N];static int[] q=new int[N];public static void main(String[] args)throws IOException{BufferedReader in=new BufferedReader(new InputStreamReader(System.in));String[] strs=in.readLine().split(" ");int n=Integer.parseInt(strs[0]);int k=Integer.parseInt(strs[1]);strs=in.readLine().split(" ");for(int i=0;i<n;i++){a[i]=Integer.parseInt(strs[i]);}// 求滑动窗口最小值int hh=0,tt=-1;for(int i=0;i<n;i++){while(hh<=tt&&q[hh]<i-k+1)hh++;while(hh<=tt&&a[q[tt]]>=a[i])tt--;q[++tt]=i;if(i>=k-1)System.out.print(a[q[hh]]+" ");}System.out.println();// 求滑动窗口最大值hh=0;tt=-1;for(int i=0;i<n;i++){while(hh<=tt&&q[hh]<i-k+1)hh++;while(hh<=tt&&a[q[tt]]<=a[i])tt--;q[++tt]=i;if(i>=k-1)System.out.print(a[q[hh]]+" ");}}
}
KMP
// s[]是长文本,p[]是模式串,n是s的长度,m是p的长度// 求模式串的Next数组:
for(int i=2,j=0;i<=m;i++){while(j && p[i]!=p[j+1]) j=ne[j];if(p[i]==p[j+1])j++;ne[i]=j;
}// 匹配
for(int i=1,j=0;i<=n;i++){while(j && s[i] != p[j+1]) j=ne[j];if(s[i] == p[j+1])j++;if(j==m){j=ne[j];}
}
应用场景
- 字符串匹配
Tire树
// 插入一个字符串
import java.io.*;public class Main{static int N=100010;static int[][] son=new int[N][26];static int[] cnt=new int[N];// 0号点既是根结点,又是空节点// son[][]存储树中每个节点的子节点// cnt[]存储以每个节点结尾的单词数量static int idx;public static void main(String[] args)throws IOException{BufferedReader in=new BufferedReader(new InputStreamReader(System.in));int n=Integer.parseInt(in.readLine());String[] strs;for(int i=0;i<n;i++) {strs=in.readLine().split(" ");char op=strs[0].charAt(0);String str=strs[1];if(op=='I') {insert(str);}else {System.out.println(query(str));}}}// 插入一个字符串static void insert(String str) {int p=0;for(int i=0;i<str.length();i++) {int u=str.charAt(i)-'a';if(son[p][u]==0)son[p][u]=++idx;p=son[p][u];}cnt[p]++;}// 查询字符串出现的次数static int query(String str) {int p=0;for(int i=0;i<str.length();i++) {int u=str.charAt(i)-'a';if(son[p][u]==0)return 0;p=son[p][u];}return cnt[p];}
}
应用场景
- 查询字符串出现过多少次
并查集
(1)朴素并查集
int p[N]; // 存储每个点的祖宗节点// 返回x的祖宗节点
int find(int x){if(p[x]!=x)p[x]=find(p[x]);return p[x];
}// 初始化,假定节点编号时1~n
for(int i=1;i<=n;i++) p[i]=i;// 合并a和b所在的两个集合
p[find(a)]=find(b);
(2)维护size的并查集
int p[N],size[N];
// p[]存储每个点的祖宗节点,size[]只有祖宗节点的有意义,表示祖宗节点所在集合中的点的数量// 返回x的祖宗节点
int find(int x){if(p[x]!=x)p[x]=find(p[x]);return p[x];
}// 初始化,假定节点编号是1~n
for(int i=1;i<=n;i++){p[i]=i;size[i]=1;
}// 合并a和b所在的两个集合
size[find(b)] += size[find(a)];
p[find(a)] =find(b);
(3) 维护到祖宗节点距离的并查集
int p[N],d[N];
// p[]存储每个点的祖宗节点,d[x]存储x到p[x]的距离// 返回x的祖宗节点
int find(int x){if(p[x]!=x){int u=find(p[x]);d[x]+=d[p[x]];p[x]=u;}return p[x];
}// 初始化,假定节点编号是1~n
for(int i=1;i<=n;i++){p[i]=i;d[i]=0;
}// 合并a和b所在的集合
p[find(a)]=find(b);
d[find(a)]=distance; //根据具体问题,初始化find(a)的偏移量
堆
java用优先队列来模拟堆
import java.io.*;
import java.util.*;public class Main{public static void main(String[] args)throws IOException{BufferedReader in=new BufferedReader(new InputStreamReader(System.in));Queue<Integer> q=new PriorityQueue<>();}
}
哈希
java HashSet
import java.io.*;
import java.util.*;class PII{int x;int y;public PII(int x,int y) {this.x=x;this.y=y;}
}public class Main{public static void main(String[] args)throws IOException{BufferedReader in=new BufferedReader(new InputStreamReader(System.in));HashSet<PII> hs=new HashSet<PII>();int n=Integer.parseInt(in.readLine());String[] strs;for(int i=0;i<n;i++) {strs=in.readLine().split(" ");int x=Integer.parseInt(strs[0]);int y=Integer.parseInt(strs[1]);hs.add(new PII(x,y));}for(PII p:hs) {System.out.println(p.x+" "+p.y);}}
}
邻接表
import java.io.*;
import java.util.*;public class Main{static int N=100010;static int[] head=new int[N];static int[] e=new int[N];static int[] ne=new int[N];static int[] w=new int[N];static int idx;static int m;public static void main(String[] args)throws IOException{BufferedReader in=new BufferedReader(new InputStreamReader(System.in));m=Integer.parseInt(in.readLine());String[] strs;Arrays.fill(head, -1);for(int i=0;i<m;i++) {strs=in.readLine().split(" ");int a=Integer.parseInt(strs[0]);int b=Integer.parseInt(strs[1]);int c=Integer.parseInt(strs[2]);add(a,b,c);}}static void add(int a,int b,int c) {e[idx]=b;w[idx]=c;ne[idx]=head[a];head[a]=idx++;}
}
树与图的遍历
深度优先遍历
int dfs(int u){st[u]=true;for(int i=h[u];i!=-1;i=ne[i]){int j=ne[i];if(!st[j])dfs(j);}
}
宽度优先遍历
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 topsort(){int hh=0,tt=-1;//d[i]存储点i的入度for(int i=1;i<=n;i++){if(d[i]==0){q[++tt]=i;}}while(hh<=tt){int t=q[hh++];for(int i=h[t];i!=-1;i=ne[i]){int j=e[i];if(--d[j]==0)q[++tt]=j;}}return tt==n-1;
}
朴素dijkstra
int g[N][N]; //存储每条边
int dist[N]; //存储1号点到每个点的最短距离
bool st[N]; //存储每个点的最短路时候已经确定// 求1号点到n号点的最短路,如果不存在就返回-1
int dijkstra(){memset(dist,0x3f,sizeof dist);dist[1]=0;for(int i=0;i<n-1;i++){int t=-1; // 在还未确认最短路的点中,寻找距离最小的点for(int j=1;j<=n;j++){if(!st[j]&&(t==-1||dist[t]>dist[j])t=j;}// 用t更新其他点的距离for(int j=1;j<=n;j++){dist[j]=min(dist[j],dist[t]+g[t][j]);}st[t]=true;}if(dist[n]==0x3f3f3f3f) return -1;return dist[n];
}
堆优化版dijkstra
typedef pair<int,int> PII;int n; // 点的数量
int h[N],w[N],e[N],ne[N],idx; // 邻接表存储所有边
int dist[N]; // 存储所有点到1号点的距离
bool st[N]; // 存储每个点的最短距离是否已确定// 求1号点到n号点的最短距离,如果不存在,则返回-1
int dijkstra(){memset(dist,0x3f,sizeof dist);dist[1]=0;priority_queue<PII,vector<PII>,greater<PII>> heap;heap.push({0,1}) //first存储距离,second存储节点编号while(heap.size()){auto t=heap.top();heap.pop();int ver = t.second,distance=t.first;if(st[ver]) continue;st[ver]=true;for(int i=h[ver];i!=-1;i=ne[i]){int j=e[i];if(dist[j]>distance+w[i]){dist[j]=distance+w[i];heap.push({dist[j],j});}}}if(dist[n]==0x3f3f3f3f) return -1;return dist[n];
}
spfa求最短路
int n; // 总点数
int h[N],w[N],e[N],ne[N],idx; // 邻接表存储所有边
int dist[N],cnt[N]; // 存储每个点到1号点的最短距离
bool st[N]; //存储每个点是否在队列中// 求1号点到n号点的最短路距离,如果从1号点无法走到n号点则返回-1
int spfa(){memset(dist,0x3f,sizeof dist);dist[1]=0;queue<int> q;q.push(1);st[1]=true;while(q.size()){auto t=q.front();q.pop();st[t]=false;for(int i=h[t];i!=-1;i=ne[i]){int j=e[i];if(dist[j]>dist[t]+w[i]){dist[j]=dist[t]+w[i];if(!st[j]){ // 如果队列中已存在j,则不需要将j重复插入q.push(j);st[j]=true;}}}}if(dist[n] == 0x3f3f3f3f) return -1;return dist[n];
}
spfa判断负环
int n; // 总点数
int h[N],w[N],e[N],ne[N],idx; // 邻接表存储所有边
int dist[N],cnt[N]; // dist[x]存储1号点到x的最短距离,cnt[x]存储1到x的最短路中经过的点数
bool st[N]; // 存储每个点是否在队列中// 如果存在负环,则返回true,否则返回false;
bool spfa(){// 不需要初始化dist数组// 原理:如果某条最短路径上有n个点(除了自己),那么加上自己之后一共有n+1个点,由抽屉原理一定有两个点相同,所以存在环。queue<int> q;for(int i=1;i<=n;i++){q.push(i);st[i]=true;}while(q.size()){auto t=q.front();q.pop();st[t]=false;for(int i=h[t];i!=-1;i=ne[i]){int j=e[i];if(dist[j]>dist[t]+w[i]){dist[j]=dist[t]+w[i];cnt[j]=cnt[t]+1;if(cnt[j]>=n) return ture; // 如果从1号点到x的最短路中包含至少n个点(不包括自己),则说明存在环if(!st[j]){q.push(j);st[j]=true;}}}}return false;
}
floyd
多源最短路
初始化:for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){if(i==j) d[i][j]=0;else d[i][j]=INF;}}// 算法结束后,d[a][b]表示a到b的最短距离
void floyd(){for(int k=1;k<=n;k++){for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){d[i][j]=min(d[i][j],d[i][k]+d[k][j]);}}}
}
朴素版prim算法
适用于点数较小的求生成树
int n; // n表示点数
int g[N][N]; // 邻接矩阵,存储所有边
int dist[N]; //存储其他点到当前最小生成树的距离
bool st[N]; //存储每个点是否已经在生成树中//如果图不连通,则返回INF(值是0x3f3f3f3f),否则返回最小生成树的树边权重之和
int prim(){memset(dist,0x3f,sizeof dist);int res=0;for(int i=0;i<n;i++){int t=-1;for(int j=1;j<=n;j++){if(!st[j]&&(t==-1 || dist[t]>dist[j])t=j;}if(i && dist[t] == INF) return INF;if(i) res += dist[t];st[t] = true;for(int j=1;j<=n;j++)dist[j]=min(dist[j],g[t][j]);}
}
Kruskal算法
适用于点数较大的求最小生成树
int n,m; // n是点数,m是边数
int p[N]; // 并查集的父节点数组class Edge Comparable<Edge>{ // 存储边int a;int b;int w;public Edge(int a,int b,int w){this.a=a;this.b=b;this.w=w;}public int compareTo(Edge o){return Integer.compare(w,o.w);}
}Edge[] edges=new Edge[M];int find(int x) //并查集核心操作
{if(p[x]!=x) p[x]=find(p[x]);return p[x];
}int kruskal(){Arrays.sort(edges,edges+m);for(int i=1;i<=n;i++) p[i]=i; //初始化并查集int res=0,cnt=0;for(int i=0;i<m;i++){int a=edgs[i].a,b=edges[i].b,w=edges[i].w;a=find(a);b=find(b);if(a!=b){p[a]=b;res+w;cnt++;}}if(cnt<n-1)return INF;return res;
}
染色法判别二分图
int n; // n表示点数
int h[N],e[M],ne[M],idx; // 邻接表存储图
int color[N]; //表示每个点的颜色,-1表示未染色,0表示白色,1表示黑色// 参数: u表示当前节点,c表示当前点的颜色
bool dfs(int u,int c){color[u]=c;for(int i=h[u];i!=-1;i=ne[i]){int j=e[i];if(color[j] == -1){if(!dfs(j,!c)) return false;}else if(color[j] == c) return false;}return true;
}bool check(){memset(color,-1,sizeof color);bool flag=true;for(int i=1;i<=n;i++){if(color[i]==-1){if(!dfs(i,0)){flag=false;break;}}}return flag;
}
匈牙利算法
int n1,n2; // n1表示第一个集合中的点数,n2表示第二个集合中的点数
int h[N],e[M],ne[M],idx; // 邻接表存储所有边,匈牙利算法中只会用到从第一个集合指向第二个集合的边,所以这里只用存一个方向的边
int match[N]; // 存储第二个集合中的每个点当前匹配的第一个集合中的点是哪个
bool st[N]; // 表示第二个集合中的每个点是否已经被遍历过bool find(int x){for(int i=h[x];i!=-1;i=ne[i]){int j=e[i];if(!st[j]){st[j]=true;if(match[j]==0 || find(match[j])){match[j]=x;return true;}}}return false;
}// 求最大匹配数,依次枚举第一个集合中的每个点能否匹配第二个集合中的点
int res=0;
for(int i=1;i<=n1;i++){memset(st,false,sizeof st);if(find(i))res++;
}
试除法判定质数
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;
}
试除法分解质因数
void divide(int x){for(int i=2;i<=x/i;i++){if(x%i==0){int s=0;while(x%i==0) x/=i,s++;System.out.println(i+" "+s);}}if(x>1)System.out.println(x+" "+1);
}
朴素筛法求素数
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;}}
}
试除法求所有约数
vector<int> get_divisors(int x){vector<int> res;for(int i=1;i<=x/i;i++){if(x%i==0){res.push_back(i);if(i!=x/i)res.push_back(x/i);}}sort(res.begin(),res.end());return res;
}
约数个数和约数之和
如果 N = p 1 c 1 ∗ p 2 c 2 ∗ . . . ∗ p k c k 约数个数: ( c 1 + 1 ) ∗ ( c 2 + 1 ) ∗ . . . ∗ ( c k + 1 ) 约数之和: ( p 1 0 + p 1 1 + . . . + p 1 c 1 ) ∗ . . . ∗ ( p k 0 + p k 1 + . . . + p k c k ) 如果N=p_1^{c_1}*p_2^{c_2}*...*p_k^{c_k} \\ 约数个数:(c_1+1)*(c_2+1)*...*(c_k+1) \\ 约数之和:(p_1^0+p_1^1+...+p_1^{c_1})*...*(p_k^0+p_k^1+...+p_k^{c_k}) 如果N=p1c1∗p2c2∗...∗pkck约数个数:(c1+1)∗(c2+1)∗...∗(ck+1)约数之和:(p10+p11+...+p1c1)∗...∗(pk0+pk1+...+pkck)
欧几里得算法
int gcd(int a,int b){return b?gcd(b,a%b):a;
}
求欧拉函数
求某个数的欧拉函数
如果 N = p 1 a 1 p 2 a 2 . . . p m a m ϕ ( N ) = N × p 1 − 1 p 1 × p 2 − 1 p 2 × . . . × p m − 1 p m 如果N=p_1^{a_1}p_2^{a_2}...p_m^{a_m} \\ \phi(N)=N \times \frac{p_1-1}{p_1} \times \frac{p_2-1}{p_2} \times ... \times \frac{p_m-1}{p_m} 如果N=p1a1p2a2...pmamϕ(N)=N×p1p1−1×p2p2−1×...×pmpm−1
int phi(int x){int res=x;for(int i=2;i<=x/i;i++){if(x%i==0){res=res/i*(i-1);while(x%i==0)x/=i;}}if(x>1)res=res/x*(x-1);return res;
}
筛法求欧拉函数
求1-n的每个数的欧拉函数
int primes[N],cnt; // primes[]存储所有素数
int euler[N]; // 存储每个数的欧拉函数
bool st[N]; // st[x]存储x是否被筛掉void get_eulers(int n){euler[1]=1;for(int i=2;i<=n;i++){if(!st[i]){primes[cnt++]=i;euler[i]=i-1;}for(int j=0;primes[j]<=n/i;j++){int t=primes[j]*i;st[t]=true;if(i%primes[j]==0){euler[t]=euler[i] * primes[j];break;}euler[t]=euler[i]*(primes[j]-1);}}
}
快速幂
求 m k m o d p m^kmodp mkmodp,时间复杂度为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;
}
扩展欧几里得算法
// 求x,y,使得ax+by=gcd(a,b)
int exgcd(int a,int b,int &x,int &y){if(!b){x=1;y=0;return a;}int d=exgcd(b,a%b,y,x);y-=(a/b)*x;return d;
}
高斯消元
// a[N][N]是增广矩阵
int gauss(){int c,r;for(c=0,r=0;c<n;c++){int t=r;for(int i=r;i<n;i++) // 找到绝对值最大的行if(fabs(a[i][c])>fabs(a[t][c])){t=i;}if(fabs(a[t][c])<eps)continue;for(int i=c;i<=n;i++) swap(a[t][i],a[r][i]); //将绝对值最大的行换到最顶端for(int i=n;i>=c;i--) a[r][i]/=a[r][c]; // 将当前行的首位变成1for(int i=r+1;i<n;i++){ // 用当前行将下面所有的列消成0if(fabs(a[i][c])>eps){for(int j=n;j>=c;j--){a[i][j]-=a[r][j]*a[i][c];}}}r++;}if(r<n){for(int i=r;i<n;i++){if(fabs(a[i][n]>eps)return 2; // 无解}return 1; //有无穷组解}for(int i=n-1;i>=0;i--){for(int j=i+1;j<n;j++){a[i][n]-=a[i][j]*a[j][n];}}return 0;// 有唯一解
}
递推求组合数
// c[a][b]表示从a个苹果中选b个的方案数
for(int i=0;i<N;i++){for(int j=0;j<=i;j++){if(!j)c[i][j]=1;else c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;}
}
来源:https://www.acwing.com/