【无标题】

一.分治

二.前缀和,差分,离散化

1.前缀和

求区间和常用

s[i]=s[i-1]+a[i]

s[i][j]=a[i][j]+s[i-1][j]+s[i][j-1]-s[i-1][j-1]

查询

s[r]---s[l-1]

二维查询一个矩形:

例题1:一维前缀和

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,a[100005],sum[100005];
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		sum[i]=sum[i-1]+a[i];
	}
	cin>>m;
	for(int i=0;i<m;i++){
		int x,y;
		cin>>x>>y;
		cout<<sum[y]-sum[x-1]<<endl;
	} 
}

例题2:二维前缀和

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,a[125][125],sum=-1e9;
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cin>>a[i][j]; 
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1];
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			for(int x1=1;x1<=n;x1++){
				for(int x2=1;x2<=n;x2++){
					if(x1<i||x2<j)continue;
					else {
						sum=max(sum,a[x1][x2]-a[x1][j-1]-a[i-1][x2]+a[i-1][j-1]);
					}
				}
			}
		}
	}
	cout<<sum<<'\n';
	return 0;
}

2.差分

d[i]=a[i]-a[i-1]

给某个区间加一个值,用差分数组操作

                      a: 1  2  3  4  5

                      d: 1  1  1  1  1

[1,3]+1              2  1  1  0  1

[2,4]+1              2  2  1  0  0

[3,5]+1              2  2  2  0  -1

求前缀和           2  4  6  6  6--->a'

第一项加一,后一项减一

d[l]+=d;
d[r+1]-=d;
d[i]不变

做题一般是求出差分数组,修改差分数组,对差分数组进行求前缀和  

a->d->d'->a'

二维差分:

a[i][j]=s[i][j]-s[i-1][j]-s[i][j-1]+s[i-1][j-1]

例题1:一维差分

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,p,l,r,fen,min1,a[5000005],s[5000005],s1[500005];
signed main()
{
	ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>p;
    for(int i=1;i<=n;i++){
    	cin>>s[i];
	}
	for(int i=1;i<=n;i++){
		a[i]=s[i]-s[i-1];
	}
	for(int i=0;i<p;i++){
		cin>>l>>r>>fen;
		if(l>r){
			int t=0;
			t=l;
			l=r;
			r=t;
		}
		if(l==0)l++;
		a[l]+=fen;
		a[r+1]-=fen;
	}
	for(int i=1;i<=n;i++){
		s1[i]=s1[i-1]+a[i];
	}
	min1=s1[1];
	for(int i=1;i<=n;i++){
		min1=min(s1[i],min1);
	}
	cout<<min1<<'\n';
	return 0;
}

例题2:二维差分

#include <iostream>
#include <vector>
#include <string>
#include <cmath>
#include <algorithm>
#include <iomanip>
#include <map>
#include <vector>
#include <queue>
#include <set>
//#include<bits/stdc++.h>
using namespace std;
#define int long long
int n, m, x1, z, x2, y2, s[1005][1005], a[1005][1005];
signed main()
{
	cin >> n >> m;
	for (int i = 1;i <= m;i++) {
		cin >> x1 >> z >> x2 >> y2;
		a[x1][y2 + 1]--;
		a[x1][z]++;
		a[x2 + 1][y2 + 1]++;
		a[x2 + 1][z]--;
	}
	for (int i = 1;i <= n;i++) {
		for (int j = 1;j <= n;j++) {
			s[i][j] = a[i][j] + s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
		}
	}
	for (int i = 1;i <= n;i++) {
		for (int j = 1;j <= n;j++) {
			cout << s[i][j] << " ";
		}
		cout << endl;
	}
	return 0;
}

3.离散化

步骤:

  1. 传入ls[top++] 
  2. 进行排序,小下标对应小数字
  3. unique去重,只能一个下标对应一个值----->它会把重复的元素添加到容器末尾,而返回值是去重之后的尾地址(是地址!!)
    int num[10]={1,1,2,2,2,3,4,5,5,5};
    int ans=unique(num,num+10)-num;  //去重函数返回地址为:去重后最后一个不重复元素地址
    
  4. 查询,将原数组x[i]的值换成ls数组值对应的下标
    for(int l=0;l<n;l++){
    			x[l].i=lower_bound(ls,ls+end,x[l].i)-ls;
    			x[l].j=lower_bound(ls,ls+end,x[l].j)-ls;
    		}

例题1:P1955

#include<bits/stdc++.h>
#define int long long
using namespace std;
int t,fa[1000005],ls[3000005];
bool p;
struct Mystruct
{
	int i1,j1,e;
}aa[1000001];
bool cmd(Mystruct a,Mystruct b){
	return a.e>b.e;
}
void csh(int d){
	for(int i=1;i<=d;i++){
		fa[i]=i;
	}
}
int find(int d){
	if(fa[d]==d) return d;
	return fa[d]=find(fa[d]);
}
void link(int d,int e){
	int l=find(d);
	int r=find(e);
	fa[l]=r;
}
signed main(){
	cin>>t;
	int top=0;
	for(int i=0;i<t;i++){
		int n;
		p=0;
		top=0;
		cin>>n;
		memset(fa, 0, sizeof(fa));
		memset(ls, 0, sizeof(ls));
		memset(aa, 0, sizeof(aa));
		for(int j=1;j<=n;j++){
			cin>>aa[j].i1>>aa[j].j1>>aa[j].e;
			ls[top++]=aa[j].i1;
			ls[top++]=aa[j].j1;
		}
		sort(ls,ls+top);
		int znd=unique(ls,ls+top)-ls;
		for(int j=1;j<=n;j++){
			aa[j].i1=lower_bound(ls,ls+znd,aa[j].i1)-ls;
			aa[j].j1=lower_bound(ls,ls+znd,aa[j].j1)-ls;
		}
		csh(znd);
		sort(aa+1,aa+1+n,cmd);
		for(int j=1;j<=n;j++){
			if(aa[j].e){
				link(aa[j].i1,aa[j].j1);
				//fa[find(aa[j].i1)]=find(aa[j].j1);
			}
			else if(find(aa[j].i1)==find(aa[j].j1)){
				printf("NO\n");
				p=1;
				break;
			}
		}
		if(p==0){
			printf("YES\n");
		}
	}
	return 0;
}

三.深度优先搜索dfs

一条路走到黑

例题1:P1036 选数

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,k,a[25],sum,ans;
bool ss(int x){
	if(x<=1)return 0;
	for(int i=2;i*i<x;i++){
		if(x%i==0)return 0;
	}
	return 1;
}
void dfs(int sum,int y,int x){//有y用于标记到哪里了,不重复取前面的数
	if(x==k){
		if(ss(sum)){
			ans++;
		    return;
		}
	}
	for(int i=y;i<n;i++){
		sum+=a[i];
		dfs(sum,i+1,x+1);
		sum-=a[i];
	}
}
signed main(){
	ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>k;
    for(int i=0;i<n;i++){
    	cin>>a[i];
	}
	dfs(0,0,0);
	cout<<ans<<'\n';
    return 0;
}

 例题2:B3621 枚举元组

不需要标记到哪了,可以重复取前面的数

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,k,a[10];
vector<int>res;
void dfs(int x){
	if(x==n){
		for(int i=0;i<n;i++){
			cout<<res[i]<<" ";
		}
		cout<<'\n';
		return;
	}
	for(int i=1;i<=k;i++){
		res.push_back(a[i]);
		dfs(x+1);
		res.pop_back();
	}
}
signed main(){
	ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>k;
    for(int i=1;i<=k;i++){
    	a[i]=i;
	}
	dfs(0);
    return 0;
}

 例题3: P1596 Lake Counting S       

找水坑 从有开始找

#include <iostream>
#include <vector>
#include <string>
#include <cmath>
#include <algorithm>
#include <iomanip>
#include <map>
#include <vector>
#include <queue>
#include <set>
//#include<bits/stdc++.h>
using namespace std;
#define int long long
int n, m, ans;
bool res;
//vector<vector<char>> a;
//vector<vector<bool>> b;
char a[105][105];
bool b[105][105];
int z[8][2] = { {-1,-1}, {-1,0}, {-1,1}, {0,-1}, {0,1}, {1,-1}, {1,0}, {1,1} };
void dfs(int sx, int sy) {
	b[sx][sy] = 1;
	for (int i = 0;i < 8;i++) {
		int tx = sx + z[i][0];
		int ty = sy + z[i][1];
		if (tx >= 0 && tx < n && ty >= 0 && ty < m && a[tx][ty] == 'W' && b[tx][ty] == 0) {
			//b[tx][ty] = 1;
			dfs(tx, ty);
		}
	}
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> m;
	//a.resize(n,vector<char>(m));
	//b.resize(n,vector<bool>(m,0));
	for (int i = 0;i < n;i++) {
		for (int j = 0;j < m;j++) {
			cin >> a[i][j];
		}
	}
	for (int i = 0;i < n;i++) {
		for (int j = 0;j < m;j++) {
			if (a[i][j] == 'W' && b[i][j] == 0) {
				dfs(i, j);//进入一个水坑,进行寻找周围是否有连着的W
				ans++;//出来则ans++说明结束一个水坑
			}
		}
	}
	cout << ans;
	return 0;
}

例题4:P10483 小猫爬山 

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,w,sum[20],a[20],ans,cnt;//sum[i]每个车的容量,a[i]小猫重量 
bool b;
bool cmp(int a, int b) {
	return a > b;
}
void dfs(int che,int cat){
	if(cat==n+1){//达到猫总量return 
		b=1;
		cout<<i<<'\n';
		return;
	}
	if(b==1) return ;
	for(int i=1;i<=che;i++){
		if(sum[i]-a[cat]>=0){
			sum[i]-=a[cat];
			dfs(che,cat+1);
			sum[i]+=a[cat];
		}
	}
}
signed main(){
	ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>w;
    for(int i=1;i<=n;i++){
    	cin>>a[i];
    	ans+=a[i];
	}
	for(int i=1;i<=n;i++){
		sum[i]=w;//每个车都是w容量 
	}
	sort(a+1,a+n+1,cmp);//排序 
	if(ans%w==0)cnt=ans/w;
	else cnt=ans/w+1;//求到车最的小需求量cnt 
	for(int i=cnt;i<=n;i++){
		b=0;
		dfs(i,1);//遍历,从cnt开始到最大需求量n,猫是会在dfs中++的 
		if(b){
			cout<<i<<'\n';
			break;
		}
	}
    return 0;
}

例题5:B3625 迷宫寻路

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n, m;
bool b[105][105], res;
vector<vector<char>> a;
int z[4][2] = {{1,0}, {0,1}, {-1,0}, {0,-1}};

void dfs(int sx, int sy) {
    if (sx == n-1 && sy == m-1) {
        res = 1;
    }
    for (int i = 0; i < 4; i++) {
        int tx = sx + z[i][0];
        int ty = sy + z[i][1];
        if (tx >= 0 && tx < n && ty >= 0 && ty < m && !b[tx][ty] && a[tx][ty] != '#') {
            b[tx][ty] = 1;
            dfs(tx, ty);
            //b[tx][ty] = 0;走过是墙 不需要再走一遍 不用标记回去
        }
    }
}
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m;
    a.resize(n, vector<char>(m));
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cin >> a[i][j];
        }
    }
    b[0][0] = 1;
    if(a[0][0]=='#'){
    	cout << "No" << '\n';
    	return 0;
	}
    dfs(0, 0);
    if (res) cout << "Yes" << '\n';
    else
	cout << "No" << '\n';
    return 0;
}

四.广度优先搜索bfs

找最短时间/路径

http://t.csdnimg.cn/xqjY3

一般会用到

queue<pair<int,int>>q;
while(!q.empty()){
    	int x1=q.front().first;
    	int y1=q.front().second;
    	q.pop();
    	for(int i=0;i<8;i++){
    		int tx=x1+z[i][0];
    		int ty=y1+z[i][1];
    		if(tx>0&&tx<=n&&ty>0&&ty<=m&&b[tx][ty]==0)
    		{
    			q.push(make_pair(tx,ty));
    			ans[tx][ty]=ans[x1][y1]+1;
    			b[tx][ty]=1;
			}
		}
	}

例题1:P1443马的遍历

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,x,y;
int z[8][2]={{-2,1},{-2,-1},{-1,2},{-1,-2},{1,2},{1,-2},{2,1},{2,-1}};//往能走的方向走
bool b[405][405];//用于标记走过的地方
int ans[405][405];
queue<pair<int,int>>q;//用于执行dfs
signed main()
{
	ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m>>x>>y;
    q.push(make_pair(x,y));
    while(!q.empty()){
    	int x1=q.front().first;
    	int y1=q.front().second;
    	q.pop();
    	for(int i=0;i<8;i++){
    		int tx=x1+z[i][0];
    		int ty=y1+z[i][1];
    		if(tx>0&&tx<=n&&ty>0&&ty<=m&&b[tx][ty]==0)
    		{
    			q.push(make_pair(tx,ty));
    			ans[tx][ty]=ans[x1][y1]+1;
    			b[tx][ty]=1;
			}
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(i==x&&j==y){
				cout<<0<<setw(5);
			}
			else if(ans[i][j]==0){
				cout<<-1<<setw(5);
			}
			else cout<<ans[i][j]<<setw(5);
		}
		cout<<'\n';
	}
	return 0;
}

例题2:P1162填涂颜色

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
int z[4][2]={{1,0},{0,1},{0,-1},{-1,0}};
bool b[35][35];
int a[35][35];
void bfs(int x,int y){
	queue<pair<int,int>>q;
	q.push(make_pair(x,y));
	while(!q.empty()){
		int x=q.front().first;
		int y=q.front().second;
		q.pop();
		//b[x][y]=1;
		for(int i=0;i<4;i++){
			int tx=x+z[i][0];
			int ty=y+z[i][1];
			if(tx>=0&&tx<n&&ty>=0&&ty<n&&a[tx][ty]==0&&b[tx][ty]==0){
				q.push(make_pair(tx,ty));
				b[tx][ty]=1;
			}
		}
	}
}

signed main()
{
	ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>n;
    for(int i=0;i<n;i++){
    	for(int j=0;j<n;j++){
    		cin>>a[i][j];
		}
	}
//外圈往里面走
	for (int i = 0; i < n; ++i) {
        if (a[i][0] == 0 && b[i][0]==0) {
            bfs(i, 0);
        }
        if (a[i][n-1] == 0 && b[i][n-1]==0) {
            bfs(i, n-1);
        }
    }
    for (int j = 0; j < n; ++j) {
        if (a[0][j] == 0 && b[0][j]==0) {
            bfs(0, j);
        }
        if (a[n-1][j] == 0 && b[n-1][j]==0) {
            bfs(n-1, j);
        }
    }
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            if (a[i][j] == 0 && b[i][j]==0) {
                cout << "2 ";
            } else {
                cout << a[i][j] << " ";
            }
        }
        cout << endl;
    }
	return 0;
}

五.STL

C++ STL详解超全总结(快速入门STL)-CSDN博客

六.并查集

模板

int find(int x)     				//查找结点 x的根结点 
{
    if(pre[x] == x) return x;		//递归出口:x的上级为 x本身,即 x为根结点        
    return pre[x] = find(pre[x]);	//此代码相当于先找到根结点 rootx,然后pre[x]=rootx 
}
void join(int x,int y)                     
{
    int fx=find(x), fy=find(y);           
    if(fx != fy)                           
        pre[fx]=fy;                        
}

 或者路径压缩(加权标记)

void union(int x,int y)
{
    x=find(x);							//寻找 x的代表元
    y=find(y);							//寻找 y的代表元
    if(x==y) return ;					//如果 x和 y的代表元一致,说明他们共属同一集合,则不需要合并,直接返回;否则,执行下面的逻辑
    if(rank[x]>rank[y]) pre[y]=x;		//如果 x的高度大于 y,则令 y的上级为 x
    else								//否则
    {
        if(rank[x]==rank[y]) rank[y]++;	//如果 x的高度和 y的高度相同,则令 y的高度加1
        pre[x]=y;						//让 x的上级为 y
    }
}

 例题1:P3367 【模板】并查集

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,z,x,y,pre[10005],r[10005];
int find(int x){
	return pre[x]==x?x:pre[x]=find(pre[x]);
}
void unions(int x,int y){
	x=find(x);
	y=find(y);

	if(r[x]>r[y]){
		pre[y]=x;
	}
	else{
		pre[x]=y;
		if(r[x]==r[y]){
			r[x]++;
		}
	}
}
signed main(){
	cin>>n>>m;
	for(int i=1;i<10005;i++){
		pre[i]=i;
		//rank[i]=1;
	}
	for(int i=0;i<m;i++){
		cin>>z>>x>>y;
		if(z==1){
			unions(x,y);
		} 
		if(z==2){
			if(find(x)==find(y)){
				cout<<"Y"<<endl;
			}
			else cout<<"N"<<endl;
		}
	}
	return 0;
}

 例题2:P1551 亲戚

#include <iostream>
#include <vector>
#include <string>
#include <cmath>
#include <algorithm>
#include <iomanip>
#include <deque>
#define int long long
const int N = 1e5 + 9;
using namespace std;
int n, m, p,pre[N],r[N];
int find(int x)
{
    return pre[x]= pre[x] == x ? x : find(pre[x]);
}
void unions(int x, int y)
{
    x = find(x);y = find(y);
    if (x==y)return;
    else
    {
        if (r[x] > r[y])swap(x, y);
        pre[x] = y;
        if (r[x] == r[y])r[y]++;
    }
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n>> m>>p;
    int x, y, a, b;
    //vector<int>x(n), y(n),a(n),b(n);
    for (int i = 0;i < n;i++)
    {
        pre[i] = i;
    }
    for (int i = 0;i < m;i++)
    {
        cin >> x>> y;
        unions(x, y);
    }
    for (int i = 0;i < p;i++)
    {
        cin >> a>> b;
        if (find(a) == find(b))
        {
            cout << "Yes" << '\n';
        }
        else cout << "No" << '\n';
    }
    return 0;
}

七.二分

二分函数

  • binary_search 查找某个元素是否出现

若在数组(要求数组元素非递减)中查找到indx元素则真,若查找不到则返回值为假

binary_search(arr,arr+size,indx)

arr: 数组首地址
size:数组元素个数
indx: 需要查找的值

  • lower_bound  查找第一个大于或等于某个元素的位置。

前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置(注意是地址)。如果所有元素都小于val,则返回last的下一位置

lower_bound(arr[],arr[]+size , indx):

arr[]: 数组首地址
size:数组元素个数
indx: 需要查找的值

 #include<bits/stdc++.h>
using namespace std;
int main(){
	int a[6]={1,2,3,2,2,2};
	sort(a,a+6);//[1,2,2,2,2,3]
	int cnt=lower_bound(a,a+6,3)-a;//得到索引位
	cout<<cnt;//5

	return 0;
} 
  • upper_bound ​ 查找第一个大于某个元素的位置。
upper_bound(arr[],arr[]+size , indx):

arr[]: 数组首地址
size:数组元素个数
indx:需要查找的值 

 #include<bits/stdc++.h>
using namespace std;
int main(){
	int a[6]={1,2,3,2,2,2};
	sort(a,a+6);//[1,2,2,2,2,3]
	int cont=upper_bound(a,a+6,2)-a;
	cout<<cont;//5

	return 0;
} 

 STL之二分查找函数_c++stl提供的二分查找函数-CSDN博客

例题1: P2249 查找

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,a[1000006],x; 
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n>>m;
	for(int i=0;i<n;i++){
		cin>>a[i];
	}
	for(int i=0;i<m;i++){
		cin>>x;
		if(binary_search(a,a+n,x)){
			cout<<lower_bound(a,a+n,x)-a+1<<" ";
		}
		else cout<<-1<<" ";
	}
	return 0;
}

 例题2: P1102 A-B 数对

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,c,ans,a[200005]; 
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n>>c;
	for(int i=0;i<n;i++){
		cin>>a[i];
	}
	sort(a,a+n);
	for(int i=0;i<n;i++){
		if(a[i]>c){
			int x1=upper_bound(a,a+n,a[i]-c)-a;
			int x2=lower_bound(a,a+n,a[i]-c)-a;
			if(x2!=n){
				ans+=abs(x1-x2);
			}
		}
	}
	cout<<ans;
	return 0;
}

八.最短路径算法、最小生成树算法

最短路径

P4779 【模板】单源最短路径(标准版)

#include<bits/stdc++.h>
#define int long long
using namespace std; 
int n,m,s,dist[100005];
struct node{
	int y,v;
	node(int _y,int _v){
		y=_y;
		v=_v;
	}
};
vector<node>edge[100005];
set<pair<int,int>>se;
void dij(int s){
	memset(dist,127,sizeof(dist));
	dist[s]=0;
	se.clear();
	for(int i=0;i<=n;i++){
		se.insert(make_pair(dist[i],i));
	}
	while(!se.empty()){
		int x=se.begin()->second;
		se.erase(se.begin());
		for(auto i:edge[x]){
			if(dist[x]+i.v<dist[i.y]){
				se.erase(make_pair(dist[i.y],i.y));
				dist[i.y]=dist[x]+i.v;
				se.insert(make_pair(dist[i.y],i.y));
			}
		}
	}
}
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m>>s;
    for(int i=0;i<m;i++){
    	int u,y,v;
    	cin>>u>>y>>v;
    	edge[u].push_back(node(y,v));
	}
	dij(s);
	for(int i=1;i<=n;i++){
		cout<<dist[i]<<" "; 
	}
    return 0;
}

 最短路经过的点:

#include<bits/stdc++.h>
#define int long long
using namespace std; 
int n,m,s,t,dist[100005],a[10005];
int parent[100005];//*
struct node{
	int y,v;
	node(int _y,int _v){
		y=_y;
		v=_v;
	}
};
vector<node>edge[100005];
set<pair<int,int>>se;
void dij(int s){
	for(int i=0;i<100005;i++){
		dist[i]=1e15;
	}
	dist[s]=0;
	se.clear();
	for(int i=0;i<=n;i++){
		se.insert(make_pair(dist[i],i));
	}
	while(!se.empty()){
		int x=se.begin()->second;
		se.erase(se.begin());
		for(auto i:edge[x]){
			if(dist[x]+i.v<dist[i.y]){
				se.erase(make_pair(dist[i.y],i.y));
				dist[i.y]=dist[x]+i.v;
				parent[i.y] = x;//*
				se.insert(make_pair(dist[i.y],i.y));
			}
		}
	}
}
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m>>s>>t;
    for(int i=1;i<=n;i++){
    	cin>>a[i];
	}
    for(int i=0;i<m;i++){
    	int u,y,v;
    	cin>>u>>y>>v;
    	edge[u].push_back(node(y,v));
    	edge[y].push_back(node(u,v));
	}
	dij(s);
	if(dist[t]>=1e9){
    	cout<<"Impossible" <<'\n';
	}
	else {
	// 输出最短路径经过的点
    vector<int> path;
    int current = t;
    while (current != s) {
        path.push_back(current);
        current = parent[current];
    }
    path.push_back(s);
    reverse(path.begin(), path.end());
    for (int i = 0; i < path.size(); i++) {
    	cout<<path[i]<<" ";
    }
    return 0;
}

例题:RC-u4 City 不 City 

#include<bits/stdc++.h>
#define int long long
using namespace std; 
int hlt,n,m,s,t,dist[100005],a[10005];
int parent[100005];
struct node{
	int y,v,rd;
	node(int _y,int _v,int _rd){
		y=_y;
		v=_v;
		rd=_rd;
	}
};
vector<node>edge[100005];
set<pair<int,int>>se;
void dij(int s,int t){
	for(int i=0;i<100005;i++){
		dist[i]=1e15;
	}
	dist[s]=0;
	se.clear();
	for(int i=0;i<=n;i++){
		se.insert(make_pair(dist[i],i));
	}
	while(!se.empty()){
		int x=se.begin()->second;
		se.erase(se.begin());
		for(auto i:edge[x]){
			if(dist[x]+i.v<dist[i.y]){
				se.erase(make_pair(dist[i.y],i.y));
				dist[i.y]=dist[x]+i.v;
				parent[i.y] = max(parent[x],i.rd);
				se.insert(make_pair(dist[i.y],i.y));
                if(i.y==t) hlt=x;
			}
            else if(dist[x]+i.v==dist[i.y]&&parent[x]<i.rd){
                se.erase(make_pair(dist[i.y],i.y));
				dist[i.y]=dist[x]+i.v;
				parent[i.y] = max(parent[x],i.rd);
				se.insert(make_pair(dist[i.y],i.y));
                if(i.y==t) hlt=x;
            }
		}
	}
}
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m>>s>>t;
    for(int i=1;i<=n;i++){
    	cin>>a[i];
	}
    for(int i=0;i<m;i++){
    	int u,y,v;
    	cin>>u>>y>>v;
    	edge[u].push_back(node(y,v,a[y]));
    	edge[y].push_back(node(u,v,a[u]));
	}
	dij(s,t);
	if(dist[t]>=1e9){
    	cout<<"Impossible" <<'\n';
	}
	else {
	cout<<dist[t]<<" "<<parent[hlt]; 
	}
	
    return 0;
}

 最小生成树

例题1:P3366 【模板】最小生成树

#include<bits/stdc++.h>
#define int long long
using namespace std; 
int n,m,dist[200005];
struct node{
	int y,v;
	node(int _y,int _v){
		y=_y;v=_v;
	}
};
vector<node>edge[200005];
set<pair<int,int>>se;
bool pd[100005];
int p()
{
	int n0=0,ans=0;
	for(int i=0;i<=200005;i++){
		dist[i]=1e30;
	}
	se.clear();
	dist[1]=0;
	for(int i=0;i<=n;i++){
		se.insert(make_pair(dist[i],i));	
	}
	while(!se.empty()){
		int x=se.begin()->second;
		se.erase(se.begin());
		if(dist[x]>1e9)break;//1e9!!!!!!!!!!
		n0++;
		ans+=dist[x];
		pd[x]=1;
		for(auto i:edge[x]){
			if(pd[i.y]==0&&i.v<dist[i.y]){
				se.erase(make_pair(dist[i.y],i.y));
				dist[i.y]=i.v;
				se.insert(make_pair(dist[i.y],i.y));
			}
		}
	}
	if(n0!=n){
		return -1;
	}
	else return ans;
}
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m;
    for(int i=0;i<m;i++){
    	int x,y,z;
    	cin>>x>>y>>z;
    	edge[x].push_back(node(y,z));
    	edge[y].push_back(node(x,z));
	}
	int t=p();
	if(t==-1)cout<<"orz";
	else cout<<t;
    return 0;
}

最小瓶颈路

最小瓶颈路:两个结点之间的最长边最短的一条路径

例题1:

#include <bits/stdc++.h>
#define int long long
using namespace std;
struct Edge {
    int u, v, a;
    bool operator<(const Edge &other) const {
        return a < other.a;
    }
};
vector<int> pre, rank1;
int find(int x) {
    if (pre[x] != x) {
        pre[x] = find(pre[x]); 
    }
    return pre[x];
}
void unite(int x, int y) {
    int rootX = find(x);
    int rootY = find(y);
    if (rootX != rootY) {
        if (rank1[rootX] > rank1[rootY]) {
            pre[rootY] = rootX;
        } else if (rank1[rootX] < rank1[rootY]) {
            pre[rootX] = rootY;
        } else {
            pre[rootY] = rootX;
            rank1[rootX]++;
        }
    }
}
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n, m;
    cin >> n >> m;
    vector<Edge> edges(m);
    for (int i = 0; i < m; ++i) {
        cin >> edges[i].u >> edges[i].v >> edges[i].a;
        --edges[i].u; 
        --edges[i].v; 
    }
    int s, t;
    cin >> s >> t;
    --s; 
    --t; 
    pre.resize(n);
    rank1.assign(n, 0);
    iota(pre.begin(), pre.end(), 0);
    sort(edges.begin(), edges.end());
    for (const auto &edge : edges) {
        unite(edge.u, edge.v);
        if (find(s) == find(t)) {
            cout << edge.a << '\n';
            return 0;
        }
    }
    cout << -1 << '\n';
    return 0;
}

九.动态规划

1.背包

01背包

 思路:

①考虑前i个物品,记录总体积为0,1,.......,m时的最大收益

②考虑了前i个物品,总体积为j时的情况有两种:

1.第 i 个物品没取,考虑第前 i-1个物品,总体积为 j 时的情况

2.第 i 个物品取了,考虑第前 i-1个物品,总体积为 j-v[i] 时的情况

优化:

考虑了前 i 个物品时的状态只和考虑了前i-1个物品的状态有关,前面i-2行都不需要记啦

 

将g数组赋值给f数组 

memcpy(f,g,sizeof(g));

最终优化:

完全背包 

思路:

①依旧是记录最大收益

② 考虑了前i个物品,总体积为j时的情况有两种:

1.第 i 个物品没取,考虑第前 i-1个物品,总体积为 j 时的情况

2.第 i 个物品取了,考虑第前 i 个物品,总体积为 j-v[i] 时的情况

优化:

 多重背包

小于100

  

小于1000:

分组背包

思路: 

优化:

2.简单dp

十.构造法

十一.模拟法

十二.拓扑排序

例题1:有向环图判断

例题2:字典序最小/最大的拓扑序列

 

默认大根堆 !!!

十三.二分图的最大匹配(匈牙利算法)

二分图判定 

 二分图匹配

 例题1:棋盘覆盖问题

思路: 

 例题2:最大独立集

最大独立集合=n-最大匹配数 

例题3:最小点覆盖

最小点覆盖 = 最大匹配数

二分图--最小点覆盖及证明-CSDN博客

例题4:最小路径覆盖

十四.最大流的增广路算法

最大流的增广路算法(Edmond Karp算法,通俗易懂)-CSDN博客

十五.哈夫曼树

哈夫曼树(C++实现)_哈夫曼树c++-CSDN博客

C++哈夫曼树+哈夫曼编码的实现(双完整版)_哈夫曼编码c++-CSDN博客

十六.树

十七.数学

1.组合数学

1.1加法原理
1.2乘法原理
1.3排列组合
1.4递推关系

2.关于模运算

①加法
int result = (a % mod + b % mod) % mod;
②减法  处理非负
int result = (a % mod - b % mod + mod) % mod;
③乘法
int result = (a % mod * b % mod) % mod;
④除法   要求逆元
int b_inverse = mod_inverse(b, mod); // 计算 b 的逆元
int result = (a % mod * b_inverse % mod) % mod;

3.逆元

阶乘逆元 三种 快速 方法 费马小定理 递推 线性-CSDN博客

【数论】求逆元的四种方法-CSDN博客

①扩展欧几里得算法求逆元

使用条件:基本上通用,不要求p为质数,且效率高,时间复杂度为O(log \: n)

void extend_gcd(ll a, ll b, ll &x, ll &y){
    if(b == 0){
        x = 1, y = 0;
        return;
    }
    extend_gcd(b, a % b, x, y);
    ll tmp = x;
    x = y;
    y = tmp - (a / b) * y;
}
ll mod_inverse(ll a, ll mod){
    ll x, y;
    extend_gcd(a, mod, x, y);
    return (x % mod + mod) % mod;
}
 ②费马小定理

使用条件:要求p为质数,效率也挺高,但由于扩展欧几里得算法通常情况下更优,因此该方法使用情况较少,时间复杂度为O(log \: n)

ll qmi(ll a, ll b, ll mod){
    ll res = 1;
    while(b){
        if(b & 1) res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}
ll fermat(ll a, ll mod){
    return qmi(a, mod - 2, mod);
}

 费马小定理+快速幂求逆元

 ③欧拉定理  拓展欧拉定理

使用条件:不要求p为质数,这相当于费马小定理求逆元的扩展,先求出欧拉函数,再求逆元,时间复杂度为O(sqrt(n))。  a、n互质

int get_phi(int m){//欧拉函数 (1--n-1的互质个数)
	int res=m;
	for(int i=2;i*i<=m;i++){
		if(m%i==0){
			res=res/i*(i-1);
			while(m%i==0)m/=i; 
		}
	}
	if(m>1)res=res/m*(m-1);
	return res;
}
int depow(string s,int phi){//降幂   b以字符串形式输入 底下再转stoll
	int b=0;
	bool flag = false;
	for(int i=0;s[i];i++){
		b=b*10+(s[i]-'0');
		if(b>=phi){
			flag=1;
			b%=phi;
		}
	}
	if(flag)b+=phi;
	return b;
}
int qmi(int a, int n, int m) { // 快速幂算法-->加上取模防止a^b很大溢出
    int res = 1;
    a %= m; // 确保底数在模数范围内
    while (n) {
        if (n & 1) res = (res * a) % m;
        a = (a * a) % m;
        n >>= 1;
    }
    return res;
}

简化幂模运算 

  phi-->  依次取模降幂

例题:P5091 【模板】扩展欧拉定理

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 1e9 + 7;
int a,m;
string s; //先用字符串存b因为b很大
int get_phi(int m){//欧拉函数 (1--n-1的互质个数)
	int res=m;
	for(int i=2;i*i<=m;i++){
		if(m%i==0){
			res=res/i*(i-1);
			while(m%i==0)m/=i; 
		}
	}
	if(m>1)res=res/m*(m-1);
	return res;
}
int depow(string s,int phi){//降幂 
	int b=0;
	bool flag = false;
	for(int i=0;s[i];i++){
		b=b*10+(s[i]-'0');
		if(b>=phi){
			flag=1;
			b%=phi;
		}
	}
	if(flag)b+=phi;
	return b;
}
int qmi(int a, int n, int m) { // 快速幂算法-->加上取模防止a^b很大溢出
    int res = 1;
    a %= m; // 确保底数在模数范围内
    while (n) {
        if (n & 1) res = (res * a) % m;
        a = (a * a) % m;
        n >>= 1;
    }
    return res;
}
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>a>>m>>s;
    int phi=get_phi(m);
    int b=0;
    if (s == "0") { // 任何数的 0 次方都是 1
        cout << 1 << endl;
        return 0;
    }
    if (s.size() > 18 || stoll(s) >= phi) { // 如果 s 转换成整数大于 phi
        b = depow(s, phi);
    } else {
        b = stoll(s); // 小于 phi,直接转换
    }
    int res = qmi(a, b, m); // 计算 a^b mod m
	cout<<res<<endl;
    return 0;
}
例题: 阶乘 逆元

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1e5+5;
const ll mod=998244353;
ll inv[maxn], fac[maxn];  //分别表示逆元和阶乘
//快速幂
ll quickPow(ll a,ll b){
	ll ans=1;
	while(b){
		if(b&1)
			ans=(ans*a)%mod;
		b>>=1;
		a=(a*a)%mod;
	}
	return ans;
}

void init(){
	//求阶乘
	fac[0]=1;
	for(int i=1;i<maxn;i++){
		fac[i]=fac[i-1]*i%mod;
	}
	//求逆元
	inv[maxn-1]=quickPow(fac[maxn-1],mod-2);
	for(int i=maxn-2;i>=0;i--){
		inv[i]=inv[i+1]*(i+1)%mod;
	}
}
ll C(int n,int m){
	if(m>n){
		return 0;
	}
	if(m==0)
		return 1;
	return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int main(){
	init();
	int n,m;
	scanf("%d%d",&n,&m);
	int sum=0;
	for(int i=0;i<n;i++) {
		int a;
		cin>>a;
		sum+=a-1;
	}
	m-=sum;
	printf("%lld\n",(C(m-1,n)+C(m-1,n-1))%mod);
}

4.中国剩余定理

解决一类同余方程组的问题,适合处理带有不同模数的情况

5.矩阵快速幂

① 快速幂

【算法篇-数论】快速幂_快速幂的时间复杂度是多少-CSDN博客

 

例题:p3390【模板】矩阵快速幂

 

#include <bits/stdc++.h>
#define int long long
using namespace std;
#define lc (p<<1)
#define rc (p<<1|1)
int n,k; 
const int mod=1000000007;
struct matrix{
	int c[101][101];//矩阵大小
	matrix(){memset(c,0,sizeof c);}
}A,res;//A存原始矩阵,res存计算结果 
matrix operator*(matrix &x,matrix &y){
	matrix t;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			for(int k=1;k<=n;k++){
				t.c[i][j]=(t.c[i][j]+x.c[i][k]*y.c[k][j])%mod;
			}
		}
	}
	return t;
}
void quickpow(int k){
	for(int i=1;i<=n;i++)res.c[i][i]=1;//单位矩阵最开始 因为单位矩阵乘等于该矩阵本身 
	while(k){
		if(k&1)res=res*A;
		A=A*A;
		k>>=1;
	}
}
signed main() {
    cin>>n>>k;
    for(int i=1;i<=n;i++){
    	for(int j=1;j<=n;j++){
    		cin>>A.c[i][j];
		}
	}
    quickpow(k);
    for(int i=1;i<=n;i++){
    	for(int j=1;j<=n;j++){
    		cout<<res.c[i][j]<<" ";
		}
		cout<<endl;
	}
    return 0;
}
② 矩阵加速递推
 例题:p1962斐波那契数列

#include <bits/stdc++.h>
#define int long long
using namespace std;
#define lc (p<<1)
#define rc (p<<1|1)
int n; 
const int mod=1000000007;
struct matrix{
	int c[3][3];
	matrix(){memset(c,0,sizeof c);}
}A,F;//A存原始矩阵,F为斐波那契矩阵 
matrix operator*(matrix &x,matrix &y){
	matrix t;
	for(int i=1;i<=2;i++){
		for(int j=1;j<=2;j++){
			for(int k=1;k<=2;k++){
				t.c[i][j]=(t.c[i][j]+x.c[i][k]*y.c[k][j])%mod;
			}
		}
	}
	return t;
}
void quickpow(int n){
	F.c[1][1]=F.c[1][2]=1;
	A.c[1][1]=A.c[1][2]=A.c[2][1]=1;
	while(n){
		if(n&1)F=F*A;
		A=A*A;
		n>>=1;
	}
}
signed main() {
    cin>>n;
    if(n==1){
    	cout<<1;
    	return 0;
	}
    quickpow(n-2);
    cout<<F.c[1][1];
    return 0;
}

6.数论

① 素数与整除问题

② 进制位
③ 最大公因数 最小公倍数

 最大公因数

int gcd(int a, int b) {
  while (b != 0) {
    int tmp = a;
    a = b;
    b = tmp % b;
  }
  return a;
}

最小公倍数

 

④ 欧几里得算法拓展

【数论系列】 欧几里得算法与拓展欧几里得_扩展欧几里得算法-CSDN博客

 

⑤ 分解质因数 唯一分解定理 试除法

任何一个整数都可以分解成若干个质因数相乘

P2043 质因子分解 

#include <bits/stdc++.h>
#define int long long
using namespace std;
#define lc (p<<1)
#define rc (p<<1|1)
int n,a[10005];
void dec(){
	for (int i=2;i<=n;i++)
	{
	 	int xx=i; 
		for (int j=2;j<=i;j++) 
			while (xx%j==0) {
			    a[j]++; xx/=j;
			}     
	}
}
signed main() {
    cin>>n;
    dec();
	for(int i=1;i<=n;i++){
		if(a[i])cout<<i<<" "<<a[i]<<endl;
	}
    return 0;
}
注:数论技巧

① 奇偶性 

奇数+奇数=偶数

偶数+偶数=偶数

奇数+偶数=奇数

奇数×奇数=奇数

偶数×偶数=偶数

奇数×偶数=偶数

②数字根

指的是一个数字各位数字之和,直到结果为一位数

可以通过mod 9得到:

987-->9+8+7=24-->2+4=6----->987mod 9=6

十八.线段树--二十八

十九.kmp

KMP算法详解及C++实现_kmp算法c++实现-CSDN博客

二十.欧拉图 

 

 例题1:单词接龙

 

例题2:欧拉路判断

 

例题3:字典序最小的欧拉路 

 

二十一.滑动窗口

例题1: 

 

例题2: 

  

例题3:

 

二十二.树状数组

 单点更新 区间查询

上一个位置p相加

下一个位置 

代码

例题1:比赛

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网

#include <bits/stdc++.h>
#define int long long
using namespace std;
int T,n,a[20005],tree[100005];
int llow[20010],lup[20010],rlow[20010],rup[20010],lcnt[20010],rcnt[20010];
int lowbit(int x){
	return x&(-x);//二进制位最后一个1的位置的值 
}
void update(int x, int k){
    while(x<=100000) {
	    tree[x]+= k;
     	x+= lowbit(x);//更新tree[]个数 
    }
}
int query(int p){//求和 个数 
	if(p==0){
		return 0;
	}
	return query(p-lowbit(p))+tree[p];
}
signed main()
{
    cin>>T;
    while(T--){
    	int ans=0;
    	cin>>n;
    	for(int i=1;i<=n;i++){
    		cin>>a[i];
		}
		for(int i=1;i<=n;i++){
			llow[i]=query(a[i]);//左边小于a[i]的数 
            lup[i]=query(100000)-query(a[i]-1);//左边大于a[i]的数  
            lcnt[i] = query(a[i])-query(a[i] - 1);//与a[i]相等的数 
            update(a[i],1);
		}
		for(int i=1;i<=100000;i++){
			tree[i]=0;
		}
		for(int i=n;i>=1;i--){
			rlow[i]=query(a[i]);
            rup[i]=query(100000)-query(a[i]-1);
            rcnt[i] = query(a[i])-query(a[i]-1);
            update(a[i],1);
            ans+=rlow[i]*lup[i];
        	ans+=llow[i]*rup[i];
        	ans-=lcnt[i]*rcnt[i];
		} 
		for(int i=1;i<=100000;i++){
			tree[i]=0;
		}
		cout<<ans<<'\n';
	}
    return 0;
}

二十三.倍增-最近公共祖先

倍增法求Lca(最近公共祖先)_parents[i][up] = parents[parents[i][up - 1]][up - -CSDN博客

二十四.欧拉筛、埃氏筛法

 埃氏筛法与欧拉筛(超级详解)-CSDN博客

“最小质因数 × 最大因数(非自己) = 这个合数” 

例题1:P3383 【模板】线性筛素数 

#include <bits/stdc++.h>
#define int long long
using namespace std;
int n,q;
bool a[100000010];
vector<int>b;
void ols(int n){
	a[1]=1;
	for(int i=2;i<=n;i++){
		if(!a[i])b.push_back(i);
		for(int j:b){
			if(j*i>n)break;
			a[j*i]=1;
			if(i%j==0)break;
		}
	}
}
signed main() {
	ols(100000000);
    cin>>n>>q;
    while(q--){
    	int x;cin>>x;
    	cout<<b[x-1]<<endl;
	}
    return 0;
}

二十五.st表

ST表(保姆级,简单易懂)-CSDN博客

 查询区间最大/最小值

例题:区间查询

#include <iostream>
#include <cmath>
#include <cstdio>
int num[1000005];
int st[25][1000005];//长度为i起始位置为j的最小值
#define max(a,b) (a>b?a:b)
using namespace std;
void init(int n)
{
    for(int i=1;i<=n;++i)
        scanf("%d",&num[i]);
    for(int i=1;i<=n;++i)
        st[0][i]=num[i];
    for(int i=1;(1<<i)<=n;++i)
    {
        for(int j=1;j<=n;++j)//j+(1<<i)是某一段开始,-1表示上一段的结束
            if(j+(1<<i)-1<=n)
                st[i][j]=max(st[i-1][j],st[i-1][j+(1<<(i-1))]);
    }
}
int query(int l,int r)
{
    int k=int(log(r-l+1)/log(2.0));
    return max(st[k][l],st[k][r-(1<<k)+1]);
}
int main()
{
    int n,m;
    scanf("%d",&n);
    init(n);
    scanf("%d",&m);
    int a,b;
    for(int i=0;i<m;++i)
    {
        scanf("%d%d",&a,&b);
        printf("%d\n",query(a,b));
    }
    return 0;
}

二十六.字符串

1.字符串哈希 o(1)

  

【基础算法】字符串哈希_哈希的字符串-CSDN博客

例题1: 

 

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int P = 131; 
const int MOD = 1e9 + 7;
// 计算字符串的哈希值
vector<int> ch(const string &s) {
    int n = s.size();
    vector<int> hashes(n + 1, 0);
    vector<int> pows(n + 1, 1);
    for (int i = 1; i <= n; ++i) {
        //pows[i] = pows[i - 1] * P % MOD;
        hashes[i] = (hashes[i - 1] * P + (s[i - 1] - 'a' + 1)) % MOD;
    }
    return hashes;
}
// 计算子串的哈希值
int gh(int l, int r, const vector<int> &hashes, const vector<int> &pows) {
    return (hashes[r + 1] - hashes[l] * pows[r - l + 1] % MOD + MOD) % MOD;//防止出现负数
}
signed main() {
    string s, t;
    cin >> s >> t;
    int lens = s.size();
    int lent = t.size();
    if (lent != lens + 1) {
        cout << 0 << endl;
        return 0;
    }
    vector<int> hs = ch(s);
    vector<int> ht = ch(t);
    vector<int> pows(lent, 1);
    for (int i = 1; i <= lent; ++i) {  //乘了几个P
        pows[i] = pows[i - 1] * P % MOD;
    }
    vector<int> v1;
    vector<char> v2;
    for (int i = 0; i <= lens; ++i) {
        int lt = gh(0, i - 1, ht, pows);
        int rt = gh(i + 1, lent - 1, ht, pows);
        int ls = gh(0, i - 1, hs, pows);
        int rs = gh(i, lens - 1, hs, pows);
        if (ls == lt && rs == rt) {
            v1.push_back(i);
            v2.push_back(t[i]);
        }
    }
    cout << v1.size() << endl;
    for (int i = 0; i < v1.size(); ++i) {
        cout << v1[i] << " " << v2[i] << endl;
    }
    return 0;
}

二十七.图论-三元环 四元环

 环计数问题 - OI Wiki (oi-wiki.org)

三角形:三元环统计_三元环计数-CSDN博客 

-------------------------------------------------------------------------------------------------------------------------------- 

二十八.线段树

区间修改和区间查询

例题:P3372 【模板】线段树 1

#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, m, a[100005], p;
#define lc (p<<1)
#define rc (p<<1|1)
struct node {
    int l, r, sum, add;
} tr[400005];
void pushup(int p) {
    tr[p].sum = tr[lc].sum + tr[rc].sum;
}
// 建树 
void build(int p, int l, int r) {
    tr[p] = {l, r, a[l], 0};
    if (l == r) return;
    int m = (l + r) >> 1;
    build(lc, l, m);
    build(rc, m + 1, r);
    pushup(p);
}
// 区间修改 
void pushdown(int p) {
    if(tr[p].l != tr[p].r) {//如果不是叶子节点,需要对其左右的子节点进行递归更新或查询
        tr[lc].sum += tr[p].add * (tr[lc].r - tr[lc].l + 1);
        tr[rc].sum += tr[p].add * (tr[rc].r - tr[rc].l + 1);
        tr[lc].add += tr[p].add;
        tr[rc].add += tr[p].add;
        tr[p].add = 0;
    }
}
void update(int p, int x, int y, int k) {
    if (x <= tr[p].l && tr[p].r <= y) {
        tr[p].sum += (tr[p].r - tr[p].l + 1) * k;
        tr[p].add += k;
        return;
    }
    int m = (tr[p].l + tr[p].r) >> 1;
    pushdown(p);
    if (x <= m) update(lc, x, y, k);
    if (y > m) update(rc, x, y, k);
    pushup(p);
}
int query(int p, int x, int y) {
    if (x <= tr[p].l && tr[p].r <= y) {
        return tr[p].sum;
    }
    int m = (tr[p].l + tr[p].r) >> 1;
    pushdown(p);
    int sum = 0;
    if (x <= m) sum += query(lc, x, y);
    if (y > m) sum += query(rc, x, y);
    return sum;
}
signed main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    build(1, 1, n);
    while (m--) {
        int op; cin >> op;
        if (op == 1) {
            int l, r, k;
            cin >> l >> r >> k;
            update(1, l, r, k);
        }
        if (op == 2) {
            int l, r;
            cin >> l >> r;
            int sum = query(1, l, r);
            cout << sum << endl;
        }
    }
    return 0;
}

例题:P3373 【模板】线段树 2

#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, mod, a[100005], p,q;
#define lc (p<<1)
#define rc (p<<1|1)
const int MOD = 571373;
struct node {
    int l, r, sum, mul, add;
} tr[400005];
void pushup(int p) {
    tr[p].sum = (tr[lc].sum + tr[rc].sum)%mod;
}
// 建树 
void build(int p, int l, int r) {
    tr[p] = {l, r, a[l],1,0};
    if (l == r) return;
    int m = (l + r) >> 1;
    build(lc, l, m);
    build(rc, m + 1, r);
    pushup(p);
}
// 区间修改 
void pushdown(int p) {
    if(tr[p].l != tr[p].r) {
        tr[lc].sum = (tr[lc].sum * tr[p].mul + tr[p].add * (tr[lc].r - tr[lc].l + 1)) % mod;
        tr[rc].sum = (tr[rc].sum * tr[p].mul + tr[p].add * (tr[rc].r - tr[rc].l + 1)) % mod;
        tr[lc].mul = (tr[lc].mul * tr[p].mul) % mod;
        tr[rc].mul = (tr[rc].mul * tr[p].mul) % mod;
        tr[lc].add = (tr[lc].add * tr[p].mul + tr[p].add) % mod;
        tr[rc].add = (tr[rc].add * tr[p].mul + tr[p].add) % mod;
        tr[p].mul = 1;
        tr[p].add = 0;
    }
}
void update(int p, int x, int y, int k) {
    if (x <= tr[p].l && tr[p].r <= y) {
        tr[p].sum += ((tr[p].r - tr[p].l + 1) * k)%mod;
        tr[p].add = (tr[p].add + k) % mod;
        return;
    }
    int m = (tr[p].l + tr[p].r) >> 1;
    pushdown(p);
    if (x <= m) update(lc, x, y, k);
    if (y > m) update(rc, x, y, k);
    pushup(p);
}
void update1(int p, int x, int y, int k) {
    if (x <= tr[p].l && tr[p].r <= y) {
        tr[p].sum = (tr[p].sum * k) % mod;
        tr[p].mul = (tr[p].mul * k) % mod;
        tr[p].add = (tr[p].add * k) % mod;
        return;
    }
    int m = (tr[p].l + tr[p].r) >> 1;
    pushdown(p);
    if (x <= m) update1(lc, x, y, k);
    if (y > m) update1(rc, x, y, k);
    pushup(p);
}
int query(int p, int x, int y) {
    if (x <= tr[p].l && tr[p].r <= y) {
        return tr[p].sum;
    }
    int m = (tr[p].l + tr[p].r) >> 1;
    pushdown(p);
    int sum = 0;
    if (x <= m) sum= (sum+query(lc, x, y))%mod;
    if (y > m) sum= (sum+query(rc, x, y))%mod;
    return sum;
}
signed main() {
    cin >> n >> q>> mod;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    build(1, 1, n);
    while (q--) {
        int op; cin >> op;
        if (op == 2) {
            int l, r, k;
            cin >> l >> r >> k;
            update(1, l, r, k);
        }
        if(op==1){
        	int l, r, k;
            cin >> l >> r >> k;
            update1(1, l, r, k);
		}
        if (op == 3) {
            int l, r;
            cin >> l >> r;
            int sum = query(1, l, r);
            cout << sum << endl;
        }
    }
    return 0;
}

二十九.字典树

代码模板

const int N=100010;
int n;
char s[N];
int ch[N][26],cnt[N],idx;
 
void insert(char *s){
  int p=0;
  for(int i=0; s[i]; i ++){
    int j=s[i]-'a';//字母映射
    if(!ch[p][j])ch[p][j]=++idx;
    p=ch[p][j];
  }
  cnt[p]++;//插入次数
}
int query(char *s){
  int p=0;
  for(int i=0; s[i]; i ++){
    int j=s[i]-'a';
    if(!ch[p][j]) return 0;
    p=ch[p][j];
  }
  return cnt[p];
}

 例题:P8306 【模板】字典树

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=3000010;
int t,n,q;
char s[N];
int ch[N][62],cnt[N],idx;

int getnum(char x){
    if(x>='A'&&x<='Z')
        return x-'A';
    else if(x>='a'&&x<='z')
        return x-'a'+26;
    else
        return x-'0'+52;
} 
void insert(string &s) {
    int p=0,len=s.size();
    for (int i=0;i<len;i++) {
        int j=getnum(s[i]);
        if (!ch[p][j]) ch[p][j] = ++idx;
        p = ch[p][j];
        cnt[p]++;
    }
}
int query(string &s) {
    int p=0,len=s.size();
    for (int i=0;i<len;i++) {
        int j=getnum(s[i]);
        if (!ch[p][j]) return 0;
        p = ch[p][j];
    }
    return cnt[p];
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> t;
    while (t--) {
        for(int i=0;i<=idx;i++)
            for(int j=0;j<=122;j++)
                ch[i][j]=0;
        for(int i=0;i<=idx;i++)
            cnt[i]=0;

        idx = 0;
        int n, q;
        cin >> n >> q;
        for (int i = 0; i < n; i++) {
            string ss;
            cin >> ss;
            insert(ss);
        }
        for (int i = 0; i < q; i++) {
            string ss;
            cin >> ss;
            cout << query(ss) << endl;
        }
    }

    return 0;
}

 三十.ac自动机

  

 代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值