ACM模板

一、排序算法知识点

(1)快速排序

#include<iostream>
using namespace std;
int n,a[1000001];
void qsort(int l,int r)//应用二分思想
{
    int mid=a[(l+r)/2];//中间数
    int i=l,j=r;
    do{
        while(a[i]<mid) i++;//查找左半部分比中间数大的数
        while(a[j]>mid) j--;//查找右半部分比中间数小的数
        if(i<=j)//如果有一组不满足排序条件(左小右大)的数
        {
            swap(a[i],a[j]);//交换
            i++;
            j--;
        }
    }while(i<=j);//这里注意要有=
    if(l<j) qsort(l,j);//递归搜索左半部分
    if(i<r) qsort(i,r);//递归搜索右半部分
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    qsort(1,n);
    for(int i=1;i<=n;i++) cout<<a[i]<<" ";
}

(2)归并排序求逆序数

#include<bits/stdc++.h>
using namespace std;
#define N 1000001
int a[N];
int b[N];
int cnt=0;//逆序对数
void merge(int l,int mid,int r){
	int i=l,j=mid+1,p=1;
	while(i<=mid&&j<=r){
		if(a[i]>a[j]){
			cnt+=mid-i+1;
			b[p]=a[j];
			p++;
			j++;
		}
		else{
			b[p]=a[i];
			i++;
			p++;
		}
	}
	if(i>mid){
		for(int k=p;j<=r;j++,k++){
			b[k]=a[j]; 
		}	 
	}
	else if(j>r){
		for(int k=p;i<=mid;i++,k++){
			b[k]=a[i]; 
		}	 
	}
	for(int k=l,q=1;k<=r;k++,q++)a[k]=b[q];
}
void divide(int l,int r){
		if(l==r)return;
		int mid=(r+l)/2; 
		divide(l,mid);
		divide(mid+1,r);
		merge(l,mid,r);
}
int main(){
		int n;
		cin>>n;
		for(int i=1;i<=n;i++)cin>>a[i];
		divide(1,n);
		for(int i=1;i<=n;i++)cout<<a[i]<<” ”;
		cout<<endl;
		cout<<cnt;
		return 0;
}

二、动态规划dp

背包问题本质上就是选择与不选的问题。能够将原本指数级的时间复杂度降为平方级甚至更低的时间复杂度。

(1)线性dp

1.背包问题

01背包问题

01背包问题就是每一个物品只能选择一次

#include<bits/stdc++.h>
using namespace std;
int w[1009],v[1009],dp[1009];
int main()
{
    long long i,j,k,l,t,r,m,n,x,y,in,ma=0;
    long long ans=0,sum;
    cin>>n>>m;
    for(i=1;i<=n;i++)
    cin>>w[i]>>v[i];
    for(i=1;i<=n;i++){//一维
        for(j=m;j>=w[i];j--){//注意,这里是从后往前
        	dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
		}
    }
    cout<<dp[m];
	return 0;
}

完全背包问题

完全背包问题就是物品有无限个

#include<bits/stdc++.h>
using namespace std;
int v[100001];
int w[100001];
int f[100001];
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>v[i]>>w[i]; 
	} 
	for(int i=1;i<=n;i++){
		for(int j=v[i];j<=m;j++){//这里是从前往后
			f[j]=max(f[j],f[j-v[i]]+w[i]);
		}
	}
	cout<<f[m];
	return 0;
}

多重背包问题

多重背包问题就是物品就是有限个

思路1:设一个物品有m个,则我么可以选择0个,1个,2个,3个……一共有m+1种选择。

思路2:我们可以用二进制压缩解决。对于所有的数都可以压缩为一个二进制数,这样我们可以选择0个,1个,2个,4个,8个……这时候,可以减少物品的选择来实现。

思路3:优先队列优化。

2.最长上升子序列

朴素算法

时间复杂度为O(n2)

dp[i]表示的是以i结尾,最长上升子序列长度为dp[i];

状态转换方程为dp[i]=max(dp[i],dp[j]+1);

#include<bits/stdc++.h>
#define FAST ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define int long long
using namespace std;
const int N=1e6+7;
int a[N];
int dp[N];
signed main(){
	FAST;
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		dp[i]=1;
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<i;j++){
			if(a[i]>a[j])dp[i]=max(dp[i],dp[j]+1);
		}
	} 
	int anser=0;
	for(int i=1;i<=n;i++){
		anser=max(anser,dp[i]);
	}
	cout<<anser;
	return 0;
}

数据结构优化dp最长上升子序列

对于状态转换方程dp[i]=max(dp[i],dp[j]+1);

for(int i=1;i<=n;i++){
	for(int j=1;j<i;j++){
		if(a[i]>a[j])dp[i]=max(dp[i],dp[j]+1);
	}
} 

在第二层for循环内发现我们仅仅只是求解i前面的最大值,并且一定要满足a[i]>a[j],故我们可以用线段树or树状数组来优化时间复杂度

基于动态开点权值线段树优化最长上升子序列,线段树的下标位置作为a[i]数组的权值,线段树值作为存储dp值的媒介,其时间复杂度为O(nlogn)

#include<bits/stdc++.h>
#define FAST ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define int long long
using namespace std;
const int N=1e6+7;
int a[N];
int dp[N];
int n,idex,root;
struct node{//采用动态开点线段树,维护dp
	int l;
	int r;
	int v;	
}tree[N*10];
void pushup(int x){
	tree[x].v=max(tree[tree[x].l].v,tree[tree[x].r].v);
}
void modify(int &x,int pos,int v,int L=0,int R=1e9+7){
	if(!x){
		x=++idex;
	}
	if(L==R){
		tree[x].v=max(tree[x].v,v);
		return;
	}
	int mid=(L+R)>>1;
	if(pos<=mid)modify(tree[x].l,pos,v,L,mid);
	else modify(tree[x].r,pos,v,mid+1,R);
	pushup(x);
}
int query(int x,int l,int r,int L=0,int R=1e9+7){
	if(!x){
		return 0;
	}
	if(L>=l&&R<=r){
		return tree[x].v;
	}
	int v=0;
	int mid=(L+R)>>1;
	if(l<=mid)v=max(v,query(tree[x].l,l,r,L,mid));
	if(r>mid)v=max(v,query(tree[x].r,l,r,mid+1,R)); 
	return v;
} 
signed main(){
	FAST;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i]; 
	}
	for(int i=1;i<=n;i++){
		dp[i]=1;
		modify(root,a[i],1);
		int v=query(root,0,a[i]-1);
		dp[i]=max(dp[i],v+1);
		modify(root,a[i],dp[i]);
	}
	int anser=0;
	for(int i=1;i<=n;i++){
		anser=max(anser,dp[i]);
	}
	cout<<anser;
	return 0;
}

基于贪心与二分算法优化dp最长上升子序列,时间复杂度为O(nlogn)

思路:定义一个b数组,其表示的是当前长度上升子序列长度为i的时候,结尾最小值为b[i];

不难发现,b数组一定是严格单调递增的(因为是最长上升子序列);

#include<bits/stdc++.h>
#define FAST ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define int long long
using namespace std;
const int N=1e6+7;
int a[N],b[N]; 
int idex;
signed main(){
	FAST;
	int n;
	cin>>n;
	int anser=0;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	} 
	for(int i=1;i<=n;i++){
		if(idex==0){
			b[++idex]=a[i];
			continue;
		}
		if(b[idex]<a[i]){
			b[++idex]=a[i];
		}
		else{
			int pos=lower_bound(b+1,b+1+idex,a[i])-b;
			b[pos]=a[i];
		}
	}
	cout<<idex;	
	return 0;
}

3.最长公共子序列

最长公共子序列顾名思义就是计算两个字符串or序列的最长的公共序列

采用动态规划dp计算最长上升子序列

其状态转换方程为
$$
dp[i][j] =
\begin{cases}

dp[i-1][j-1]+1 &a[i]=a[j]\
max(dp[i][j-1],dp[i-1][j])&a[i]!=a[j]
\end{cases}
$$

#include<bits/stdc++.h>
#define FAST ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define int long long
using namespace std;
const int N=1e6+7;
int a[N];
int b[N];
int dp[2][N];//通过滚动数组压缩空间
int n;
signed main(){
	FAST;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}	
	for(int i=1;i<=n;i++){
		cin>>b[i];
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(a[i]==b[j]){
				dp[i&1][j]=dp[(i-1)&1][j-1]+1;	
			}
			else{
				dp[i&1][j]=max(dp[(i-1)&1][j],dp[i&1][j-1]);
			}
		}
	}
	cout<<dp[n&1][n];
	return 0;
}

(2)状态机模型dp

例如

大盗阿福

阿福是一名经验丰富的大盗。趁着月黑风高,阿福打算今晚洗劫一条街上的店铺。

这条街上一共有 N

家店铺,每家店中都有一些现金。

阿福事先调查得知,只有当他同时洗劫了两家相邻的店铺时,街上的报警系统才会

启动,然后警察就会蜂拥而至。

作为一向谨慎作案的大盗,阿福不愿意冒着被警察追捕的风险行窃。

他想知道,在不惊动警察的情况下,他今晚最多可以得到多少现金?

输入格式

输入的第一行是一个整数 T,表示一共有 T 组数据。

接下来的每组数据,第一行是一个整数 N,表示一共有 N 家店铺。

第二行是 N 个被空格分开的正整数,表示每一家店铺中的现金数量。每家店铺中的

现金数量均不超过 1000。

输出格式

对于每组数据,输出一行。

该行包含一个整数,表示阿福在不惊动警察的情况下可以得到的现金数量。

数据范围

1≤T≤50

1≤N≤105

输入样例:

2

3

1 8 2

4

10 7 6 14

输出样例:

8

24

样例解释

对于第一组样例,阿福选择第 2 家店铺行窃,获得的现金数量为 8。

对于第二组样例,阿福选择第 1 和 4 家店铺行窃,获得的现金数量为 10+14=24。

题解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MySNd0kO-1687510783086)(ACM模板.assets/image-20230530094839894.png)]

其中,0表示不偷,1表示偷。

如果要偷第 i 家店铺,则第 i-1 店铺不能被偷:f[i,1]=f[i-1,0]+ w i w_{i} wi;

如果不偷第 i 家店铺,则第 i-1 店铺任意安排:f[i,0]=max(f[i-1,1],f[i-1,0]);

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int T, n;
int w[N], f[N][2];
int main()
{
    scanf("%d", &T);
    while(T --)
    {
        scanf("%d", &n);
        for(int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);
        for(int i = 1; i <= n; i ++ )
        {
            f[i][0] = max(f[i - 1][0], f[i - 1][1]);
            f[i][1] = f[i - 1][0] + w[i];
        }
        printf("%d\n", max(f[n][0], f[n][1]));
    }
    return 0;
}

(3)状态压缩dp

状态压缩本质上就是将所处的状态为一串的二进制数压缩为一个10进制(16进制的数)以方便计算。

例如,长度为8位的二进制数,我们可以仅仅用0~255来替代。

状态压缩dp的模板:

炮兵阵地

司令部的将军们打算在 N×M的网格地图上部署他们的炮兵部队。

一个 N×M的地图由 N 行 M列组成,地图的每一格可能是山地(用 H 表示),也可能是平原(用 P 表示),如下图。

在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:

在这里插入图片描述

如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。

图上其它白色网格均攻击不到。

从图上可见炮兵的攻击范围不受地形的影响。

现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。

输入格式

第一行包含两个由空格分割开的正整数,分别表示 N和 M;

接下来的 N行,每一行含有连续的 M个字符(P 或者 H),中间没有空格。按顺序表示地图中每一行的数据。

输出格式

仅一行,包含一个整数 K,表示最多能摆放的炮兵部队的数量。

数据范围

N ≤ 100 , M ≤ 10 N≤100,M≤10 N100,M10

输入样例

5 4

PHPP

PPHH

PPPP

PHPP

PHHP

输出样例

6

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int g[10001];
int cnt[10001];
int f[2][10001][10021];//f[i][j][k]表示在前i行中,第i层的状态为j,第i-1行的状态为k
vector<int>state; //记录状态
int n,m;
void count(int x){//记录当前x状态时
    int sum=0;
    int y=x;
    for(int i=0;i<m;i++){
        if((x&1)==1)sum++;
        x>>=1;
    }
    cnt[y]=sum;
}
bool check(int s){
    for(int i=0;i<m;i++){
        if((s>>i&1)&&((s>>i+1&1)||(s>>i+2&1)))return false;
    }
    return true;
}
int main()
{
   
    cin>>n>>m;
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            char ch;
            cin>>ch;
            if(ch=='H')g[i]+=1<<j;
        }
    }
    for(int i=0;i<(1<<m);i++){
        if(check(i)){
            state.push_back(i);
            count(i);
           // cout<<i<<" "<<cnt[i]<<endl;
        }
    }
    for(int i=0;i<n+2;i++){
        for(int j=0;j<state.size();j++){ //枚举第i行的状态
            for(int k=0;k<state.size();k++){//枚举第i-1行状态
                for(int u=0;u<state.size();u++){//枚举第i-2行状态
                    int a=state[j],b=state[k],c=state[u];//a为第i行状态,b为i-1行状态,c为i-2行状态
                    if((a&b)||(b&c)||(a&c))continue;
                    if(g[i]&a)continue;//不满足条件的情况
                    f[i&1][j][k]=max(f[i&1][j][k],f[i-1 & 1][k][u]+cnt[a]);
                }
            }
        }
    }
    cout<<f[n+1&1][0][0];
    return 0;
}

(4)区间dp

环形石子合并

将 n堆石子绕圆形操场排放,现要将石子有序地合并成一堆。

规定每次只能选相邻的两堆合并成新的一堆,并将新的一堆的石子数记做该次合并的得分。

请编写一个程序,读入堆数 n及每堆的石子数,并进行如下计算:

选择一种合并石子的方案,使得做n-1次合并得分总和最大。选择一种合并石子的方案,使得做n-1次合并得分总和最小。

输入格式

第一行包含整数 n,表示共有 n堆石子。

第二行包含 n个整数,分别表示每堆石子的数量。

输出格式

输出共两行:

第一行为合并得分总和最小值,

第二行为合并得分总和最大值。

数据范围

1 ≤ n ≤ 200 1≤n≤200 1n200

输入样例:

4

4 5 9 4

输出样例:

43

54

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int a[401];
int dp[411][411];
int dp2[411][411];
int sum[411][411];
int main()
{
    int n;
    cin>>n;
    for (int i = 1; i <= n; i ++ ){
        cin>>a[i];
        a[n+i]=a[i];
    }
    for(int i=1;i<=2*n;i++){
        for(int j=i;j<=2*n;j++){
            for(int k=i;k<=j;k++){
                sum[i][j]+=a[k];//为了实现环形,我们可以延长两倍
            }
        }
    }
    memset(dp2,0x7f,sizeof(dp2));
    for(int i=1;i<=2*n;i++){
        dp2[i][i]=0;
    }
    for(int len=1;len<=2*n;len++){//枚举长度
        for(int j=1;j<=2*n-len+1;j++){//枚举起点
            int ends=j+len-1;//枚举终点
            for(int k=j;k<ends;k++){
                dp[j][ends]=max(dp[j][ends],dp[j][k]+dp[k+1][ends]+sum[j][ends]);
          	dp2[j][ends]=min(dp2[j][ends],dp2[j][k]+dp2[k+1][ends]+sum[j][ends]);
            }
        }
    }
    int Min=0x7fffffff;
    int Max=0;
    for (int i = 1; i <= n; i++) {
        Min = min(Min, dp2[i][i + n - 1]);
        Max = max(Max, dp[i][i + n - 1]);
    }
    cout << Min<<endl<<Max<<endl;
    return 0;
}

(5)树形dp

树的最长路径

给定一棵树,树中包含 n个结点(编号1~n)和 n-1条无向边,每条边都有一个权值。现在请你找到树中的一条最长路径。换句话说,要找到一条路径,使得使得路径两端的点的距离最远。

注意:路径中可以只包含一个点。

输入格式

第一行包含整数n。

接下来n-1行,每行包含三个整数

a i , b i , v i , 表示点 a i 和 b i 之间存在一条权值为 c i 的边。 a_{i} ,b_{i},v_{i},表示点a_{i}和b_{i}之间存在一条权值为c_{i}的边。 ai,bi,vi,表示点aibi之间存在一条权值为ci的边。

输出格式

输出一个整数,表示树的最长路径的长度。

数据范围

1 ≤ n ≤ 10000 1≤n≤10000 1n10000

1 ≤ a i , b i ≤ n 1≤ a_{i},b_{i}≤n 1ai,bin

− 1 0 5 ≤ c i ≤ 1 0 5 -10^5≤ci≤10^5 105ci105

输入样例:

6

5 1 6

1 4 5

6 3 9

2 6 8

6 1 7

输出样例:

22

题解:

树形dp简单的来说就是依照树的形状来进行计算。本题需要求最长路径,我们仅仅去找每一颗子树找最大的和第二大的叶子,然后相加即可求出最大。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
struct PII{
	int x;
	int y; 
};
vector<PII>q[10005];
bool vis[1000001];
int f[1000001];
int ans=0;
int dfs(int step){
    int len=q[step].size();
    int d1=0,d2=0;
    for(int i=0;i<len;i++){
        if(vis[q[step][i].x]==1)continue;
        vis[q[step][i].x]=1;
        int d=dfs(q[step][i].x)+q[step][i].y;
        vis[q[step][i].x]=0;
        f[step]=max(f[step],d);
        if(d>=d1)d2=d1,d1=d;
        else if(d>d2)d2=d;
    }
    ans=max(ans,d1+d2);//记录相加最大值
    return f[step];//返回最大值
}
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n-1;i++){
        int x,y,z;
        cin>>x>>y>>z;
        q[x].push_back({y,z});
        q[y].push_back({x,z});
    }
    vis[1]=1;
    dfs(1);
    cout<<ans;
    return 0;
}

三、数论

(1)筛素数

1.埃式筛法

筛去合数:任意的合数都存在一个或多个比该合数小的素数,使得该合数可以被此素数筛去。(用的不多)

2.欧拉筛

#include<bits/stdc++.h>
using namespace std;
bool vis[10000001];
int prime[10000001]; 
int main(){
	int n;
	cin>>n;
	for(int i=2;i<=n;i++){
		if(!vis[i]){
			prime[++prime[0]]=i;//prime[0]记录素数的个数,并且prime[1~n]记录素数 
		}
			for(int j=1;i*prime[j]<=n;j++){
				vis[i*prime[j]]=1;//标记为合数 
				if(i%prime[j]==0){
					break;
				} 
			}
	} 
	for(int i=1;i<=prime[0];i++){
		cout<<prime[i]<<" ";
	}
	return 0;
}

(2)RMQ算法

RMQ算法简单的来说就是在O(logn)的复杂度求区间的最大或者最小值,本质上是动态规划。

#include<bits/stdc++.h>
using namespace std;
int f[N][20];
int a[N];
const int N=2e5+7;
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    for(int j=0;j<18;j++){
        for(int i=1;i+(1<<j)-1<=n;i++){
        	if(!j){
        		f[i][j]=a[i];
			}
            else f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
        }
    }
    int m;
    cin>>m;
    for(int i=1;i<=m;i++){
        int x,y;
        cin>>x>>y;
        int len=y-x+1;
        int k=log(len)/log(2);
        cout<<max(f[x][k],f[y-(1<<k)+1][k])<<endl;;
    }
    return 0;
}

(3)欧拉筛求欧拉函数

在这里插入图片描述

本代码是求前n的欧拉函数之和

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+7; 
bool vis[N];
int prime[N];
int phi[N]; 
int main(){
	int n;
	cin>>n;
	phi[1]=1;
	for(int i=2;i<=n;i++){
		if(!vis[i]){
			prime[++prime[0]]=i;//prime[0]记录素数的个数,并且prime[1~n]记录素数 
			phi[i]=i-1;
		}
			for(int j=1;i*prime[j]<=n;j++){
				vis[i*prime[j]]=1;//标记为合数 
				if(i%prime[j]==0){
					phi[i*prime[j]]=phi[i]*prime[j];
					break;
				} 
				phi[i*prime[j]]=phi[i]*(prime[j]-1);
			}
	} 
    long long ans=0;
    for (int i = 1; i <= n; i ++ ){
        ans+=phi[i];
    }
    cout<<ans;
	return 0;
}

(4)容斥原理

给定一个整数 n 和 m 个不同的质数 p 1 , p 2 , … , p m p_{1} ,p_{2} ,…,p_{m} p1,p2,,pm

请你求出 1∼n中能被 p 1 , p 2 , … , p m p_{1}, p_{2},…,p_{m} p1,p2,,pm中的至少一个数整除的整数有多少个。

输入格式

第一行包含整数 n和 m。

第二行包含 m个质数。

输出格式

输出一个整数,表示满足条件的整数的个数。

数据范围

1 ≤ m ≤ 16 , 1 ≤ n , p i ≤ 1 0 9 1≤m≤16,1≤n,p_{i} ≤10^9 1m16,1n,pi109

输入样例:

10 2

2 3

输出样例:

7

#include<bits/stdc++.h>
using namespace std;
const int N = 19;
long long a[N];//存质数
long long num[N];//存集合
int main()
{
    long long n,m;
    scanf("%lld%lld", &n, &m);
    for(long long i=1;i<=m;i++){
        scanf("%lld", &a[i]);
        num[i]=n/a[i];
    }
    long long x=(1<<(m))-1;
    long long anser=0;
    for(long long i=1;i<=x;i++){
        long long sum=0;//1的个数
        long long ans=1;//最小公倍数
        long long xx=i;//记录
        long long p=1;//下标
        while(xx!=0){
            if(xx%2==1){
                sum++;
                if(ans<=n)
                ans=ans*a[p];
            }
            p++;
			xx/=2;
        }
        if(sum%2==1){
            anser=anser+(n/ans);
        }
        else{
            anser=anser-(n/ans);
        }
    }
    cout<<anser;
    return 0;
}

(5)欧几里得与扩展欧几里得算法

1.欧几里得算法

int gcd(int a,int b){
    return !b?a:gcd(b,a%b);
}

2.扩展欧几里得算法

void exgcd(int a, int b, int &x, int &y){
    if(b==0){
        x=1;
        y=0;
        return;
    }
    exgcd(b,a%b,x,y);
   int t=x;
    x=y;
    y=t-(a/b)*y;
}

1.若ax+by=gcd(a,b),则上面代码是此式子的一个特解。

2.若ax+by=c,若c能被gcd(a,b)整除,则有解,否则无解。

3.若ax+by=c有解,即c能被gcd(a,b)整除,则 x ′ = x ∗ ( g c d ( a , b ) c ) , y ′ = y ∗ ( g c d ( a , b ) c ) x'=x*(\frac{gcd(a,b)}{c}) , y'=y*(\frac{gcd(a,b)}{c}) x=x(cgcd(a,b)),y=y(cgcd(a,b))

(6)组合计数

1.递推求组合数

C a b = C a − 1 b − 1 + C a b − 1 C_{a}^{b}=C_{a-1}^{b-1}+C_{a}^{b-1} Cab=Ca1b1+Cab1

#include<bits/stdc++.h>
using namespace std;
const int N=2e3+7;
const int mod=1e9+7;
int c[N][N];
int main(){
    for(int i=0;i<=2001;i++){
        for(int j=0;j<=i;j++){
            if(j==0)c[i][j]=1;
            else c[i][j]=c[i-1][j-1]%mod+c[i-1][j]%mod;
        }
    }
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        int a,b;
        cin>>a>>b;
        cout<<c[a][b]%mod<<endl;
    }
    return 0;
}

2.lucas定理

C a b   m o d   p = l u c a s ( a , b , p ) = { C a b   m o d   p ( a < p 且 b < p ) ( l u c a s ( a p , b p , p ) ∗ C a m o d p b m o d p ) m o d p ( 其余情况 ) C_{a}^{b}\bmod p=lucas(a,b,p) = \begin{cases} C_{a}^{b}\bmod p &(a<p且b<p)\\ (lucas(\frac{a}{p},\frac{b}{p},p)*C_{a mod p}^{b mod p}) mod p &(其余情况) \end{cases} Cabmodp=lucas(a,b,p)={Cabmodp(lucas(pa,pb,p)Camodpbmodp)modp(a<pb<p)(其余情况)

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
long long mul(long long a,long long b,long long p){//快速乘
    long long ans=0;
    for(;b;b>>=1){
        if(b&1)ans=(ans+a)%p;
        a=a*2%p;
    }
    return ans;
}
long long power(long long a,long long b,long long p){快速逆
    long long ans=1%p;
    for(;b;b>>=1){
        if(b&1)ans=ans*a%p;
        a=a*a%p;
    }
    return ans;
}
long long inv(long long x,long long y,long long p){//求逆元
    return power(x,y,p);
}
long long C(long long a,long long b,long long p){//求组合数
    long long up=1;
    for(long long i=a;i>=a-b+1;i--){
        up=mul(up,i,p);
    }
    long long down=1;
    for(long long i=b;i>=1;i--){
        down=mul(down,i,p);
    }
    down=inv(down,p-2,p);
    return mul(up,down,p);
} 
long long lucas(long long a,long long b,long long p){//lucas算法
	if(a<p&&b<p)return C(a,b,p);
	else return mul(lucas(a/p,b/p,p),C(a%p,b%p,p),p); 
}
int main()
{
    long long t;
    cin>>t;
    while(t--){
        long long a,b,p;
        cin>>a>>b>>p;
        cout<<lucas(a,b,p)<<endl;
    }
    return 0;
}

(7)常见的数学公式

1.数学公式

1 2 + 2 2 + 3 2 + . . . + n 2 = n ( n + 1 ) ( 2 n + 1 ) 6 1^2+2^2+3^2+...+n^2=\frac{n(n+1)(2n+1)}{6} 12+22+32+...+n2=6n(n+1)(2n+1)

1 3 + 2 3 + 3 3 + . . . + n 3 = ( n ( n + 1 ) 2 ) 2 1^3+2^3+3^3+...+n^3=(\frac{n(n+1)}{2})^2 13+23+33+...+n3=(2n(n+1))2

( x + a ) n = ∑ k = 0 n c n k x k a n − k (x+a)^{n}=\sum_{k=0}^{n}c_{n}^{k}x^ka^{n-k} (x+a)n=k=0ncnkxkank

g c d ( a , b , c , . . . . ) = g c d ( a , b − a , c − b , . . . . ) gcd(a,b,c,....)=gcd(a,b-a,c-b,....) gcd(a,b,c,....)=gcd(a,ba,cb,....)

Φ ( n ) = n ∗ ( 1 − 1 p 1 ) ∗ ( 1 − 1 p 2 ) ∗ . . . ∗ ( 1 − 1 p n ) \Phi(n)=n*(1-\frac{1}{p_{1} }) *(1-\frac{1}{p_{2} }) *...*(1-\frac{1}{p_{n} }) Φ(n)=n(1p11)(1p21)...(1pn1) ( p k 为 n 的质因数 ) (p_{k}为n的质因数) (pkn的质因数)

2.卡特兰数

卡特兰数前n项

pos012345678910
v112514132429143048621679658786

卡特兰数递归定义
F ( 0 ) = 1 , F ( 1 ) = 1 ; F(0)=1,F(1)=1; F(0)=1,F(1)=1;

F ( n ) = F ( 0 ) ∗ F ( n − 1 ) + F ( 1 ) ∗ F ( n − 2 ) + . . . . . . + F ( n − 1 ) ∗ F ( 0 ) ; F(n)=F(0)*F(n-1)+F(1)*F(n-2)+......+F(n-1)*F(0); F(n)=F(0)F(n1)+F(1)F(n2)+......+F(n1)F(0);

F ( n ) = 4 n − 2 n + 1 F ( n − 1 ) F(n)=\frac{4n-2}{n+1}F(n-1) F(n)=n+14n2F(n1)

F ( n ) = 1 n + 1 C 2 n n ⇔ C 2 n n − C 2 n n − 1 F(n)=\frac{1}{n+1}C_{2n}^{n}\Leftrightarrow C_{2n}^{n}-C_{2n}^{n-1} F(n)=n+11C2nnC2nnC2nn1

卡特兰数的例题

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

答案是 C n + m n + C n + m n + k C_{n+m}^{n}+C_{n+m}^{n+k} Cn+mn+Cn+mn+k

3.斐波那契公式

斐波那契通项公式
F n = 1 5 [ ( 1 + 5 2 ) n − ( 1 − 5 2 ) n ] F_{n} =\frac{1}{\sqrt{5} } [(\frac{1+\sqrt{5}}{2})^{n}-(\frac{1-\sqrt{5}}{2})^{n}] Fn=5 1[(21+5 )n(215 )n]
矩阵快速幂求斐波那契
[ F n F n − 1 ] = [ 1 1 1 0 ] n − 1 ∗ [ F 1 F 0 ] \begin{bmatrix}F_{n}\\F_{n-1}\end{bmatrix}=\begin{bmatrix}1&1\\1&0\end{bmatrix}^{n-1}*\begin{bmatrix}F_{1}\\F_{0}\end{bmatrix} [FnFn1]=[1110]n1[F1F0]

四、图论

(1)单源最短路问题

1.Dijkstra算法

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+7;
struct node
{
  int e;
  int v;
};
vector<node>a[N];
priority_queue<node>q;
bool operator <(const node xx,const node yy){
    if(xx.v>yy.v)return 1;
    else return 0;
}
void add(int x,int y,int z){
   a[x].push_back({y,z});
}
int dis[N];
bool vis[N]; 
void dj(int xx,int yy){
    memset(dis,0x7f,sizeof(dis));
    dis[xx]=0;
    q.push({xx,0});
    while(q.size()){
        node no=q.top();
        q.pop();
        if(vis[no.e]==1)continue;
        vis[no.e]=1;
        int len=a[no.e].size();
        for(int i=0;i<len;i++){
            if(dis[a[no.e][i].e]>dis[no.e]+a[no.e][i].v){
               dis[a[no.e][i].e]=dis[no.e]+a[no.e][i].v;
               q.push({a[no.e][i].e,dis[no.e]+a[no.e][i].v});
            }
        }
    }
    cout<<dis[yy];
}
int main()
{
    int n,m,f,e;
    cin>>n>>m>>f>>e;
    while(m--){
        int x,y,z;
        cin>>x>>y>>z;
        add(x,y,z);
        add(y,x,z);
    }
    dj(f,e);
    return 0;
}

2.SPFA算法

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 1e5+7;
struct node{
    int e;
    int v;
};
vector<node>a[N];
int dis[N];
int vis[N];
queue<node>q;
void spfa(int x,int y){
    q.push({x,0});
    memset(dis,0x7f,sizeof dis);
    dis[x]=0;
    while(q.size()){
        node xx=q.front();
        q.pop();
        int len=a[xx.e].size();
        //cout<<xx.e<<" "<<len<<endl;
        for(int i=0;i<len;i++){
            if(dis[a[xx.e][i].e]>dis[xx.e]+a[xx.e][i].v){
                dis[a[xx.e][i].e]=dis[xx.e]+a[xx.e][i].v;
                q.push({a[xx.e][i].e,dis[a[xx.e][i].e]});
            }
        }
    }
    cout<<dis[y];
}
int main()
{
    int n,k,f,b;
    cin>>n>>k>>f>>b;
    for(int i=1;i<=k;i++){
        int x,y,z;
        cin>>x>>y>>z;
        a[x].push_back({y,z});
        a[y].push_back({x,z});
    }
    spfa(f,b);
    return 0;
}

SPFA求负环

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 1e5+7;
struct node{
    int e;
    int v;
};
vector<node>a[N];
int dis[N];
int vis[N];
bool spfa(int n){
    queue<node>q;
    for(int i=1;i<=n;i++){
        q.push({i,0});
        vis[i]++;
    }
    while(q.size()){
        node xx=q.front();
        q.pop();
        int len=a[xx.e].size();
        for(int i=0;i<len;i++){
            if(dis[a[xx.e][i].e]>dis[xx.e]+a[xx.e][i].v){
                dis[a[xx.e][i].e]=dis[xx.e]+a[xx.e][i].v;
                vis[a[xx.e][i].e]=vis[xx.e]+1;   //增加了这里            
                if(vis[a[xx.e][i].e]>n)return 0;
                q.push({a[xx.e][i].e,dis[a[xx.e][i].e]});
            }
        }
    }
    return 1;
}
int main()
{
    int f;
    cin>>f;
    while(f--){
        int n,m,w;
        cin>>n>>m>>w;
        for(int i=1;i<=m;i++){
            int x,y,z;
            cin>>x>>y>>z;
            a[x].push_back({y,z});
            a[y].push_back({x,z});
        }
        for(int i=1;i<=w;i++){
            int x,y,z;
            cin>>x>>y>>z;
            a[x].push_back({y,-z});
        }
        memset(dis,0,sizeof(dis));
        memset(vis,0,sizeof(vis));
        if(spfa(n)==0)cout<<"YES"<<endl;
        else cout<<"NO"<<endl;
        memset(a,0,sizeof(a));
    }
    return 0;
}

(2)Tarjan算法求强连通分量

#include<bits/stdc++.h> 
using namespace std;
const int N=1e6+7;
vector<int>a[N];
stack<int>st;
int dfn[N];
int low[N];
int t=0;
int vis[N];
void tarjan(int x){
	dfn[x]=low[x]=++t;
	vis[x]=1;
	st.push(x);
	for(int i=0;i<a[x].size();i++){
		if(!dfn[a[x][i]]){//没有访问 
			tarjan(a[x][i]);
			low[x]=min(low[x],low[a[x][i]]);//回溯找到最小
		}
		else if(vis[a[x][i]]==1){//在栈内 
			low[x]=min(low[x],dfn[a[x][i]]); //找到根节点
		} 
	}
	if(low[x]==dfn[x]){
		int xx;
		do{
			xx=st.top();
			cout<<xx<<" ";
			st.pop();
		}while(xx!=x);
		cout<<endl;
	}
}
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int x,y;
		cin>>x>>y;
		a[x].push_back(y);
	}
	tarjan(1);
	return 0;
}

(3)最小生成树

1.Kruskal算法

kruskal算法从小到大排序边权;选择最短的边构成最小生成树。

#include<bits/stdc++.h>
#define FAST ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define int long long
using namespace std;
const int N=1e6+7;
struct node{
	int b;
	int e;
	int v;
}a[N];
int pre[N];
int find(int x){
	if(pre[x]==x)return x;
	return pre[x]=find(pre[x]);
}
void merge(int x,int y){
	int X=find(x);
	int Y=find(y);
	if(X!=y){
		pre[X]=Y;
	}
}
bool cmp(const node xx,const node yy){
	return xx.v<yy.v;
}
signed main(){
	FAST;
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		pre[i]=i;
	}
	for(int i=1;i<=m;i++){
		cin>>a[i].b>>a[i].e>>a[i].v;
	} 
	sort(a+1,a+1+m,cmp);
	int ans=0;
	for(int i=1;i<=m;i++){
		if(find(a[i].b)!=find(a[i].e)){
			num++;
			merge(a[i].b,a[i].e);
			ans+=a[i].v;
		}
        
	}
	cout<<ans;
	return 0;
}

2.prim算法

prim算法就是bfs以点扩展点,选择最短的边权;当选择完n个点的时候结束,并构成最小生成树。

#include<bits/stdc++.h>
#define FAST ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define int long long
using namespace std;
const int N=1e6+7;
struct node{
	int e;
	int v;
};
vector<node>a[N];
priority_queue<node>q;
bool vis[N];
bool operator<(const node xx,const node yy){
	return xx.v>yy.v;
}
signed main(){
	FAST;
	int n,m,x,y,z;
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		cin>>x>>y>>z;
		a[x].push_back({y,z});
		a[y].push_back({x,z});	
	} 
	q.push({1,0});
	int ans=0;
	int num=0;
	while(q.size()){
		node no=q.top();
		q.pop();
		if(vis[no.e])continue;
		ans+=no.v;
		vis[no.e]=1;
		num++;
		for(int i=0;i<a[no.e].size();i++){
			q.push({a[no.e][i].e,a[no.e][i].v});
		}
		if(num==n)break;
	}
	if(num==n){
		cout<<ans;
	}
	else cout<<"orz";
	return 0;
}

(4)二分图匹配

1.匈牙利算法

五、数据结构

(1)并查集

#include <bits/stdc++.h>
using namespace std;
int sum=0;
int pre[1000001];
int vis[1000001];
/*int find(int x){//压缩寻找路径;寻找时为了避免成为单叉数,变n叉数,以节约时间 
	if(pre[x]==x)return x;
	return pre[x]=find(pre[x]);//找的时候就把该点的根节点给找到并替换 
} */
int find(int x){//暴力寻找根节点 
	while(pre[x]!=x){
		x=pre[x];
	}
	return x;
}
void join(int x,int y){
	int X=find(x);
	int Y=find(y);
	if(X!=Y){
		pre[X]=Y;//表示X的父节点为Y 
	}
} 
int main(){
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        pre[i]=i;
    }
    int x,y;
    for(int i=1;i<=m;i++){
        cin>>x>>y;
        join(x,y);
    } 
    for(int i=1;i<=n;i++){//查找每一个元素所在的强连通分量内
        vis[find(i)]=1;
    }
    for(int i=1;i<=n;i++){
        sum+=vis[i];
    }
    cout<<sum;
    return 0;
}

(2)树状数组

#include<bits/stdc++.h>
#define FAST ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define int long long
using namespace std;
const int N=1e6+7;
vector<int>nums; //y总离散化代码
int a[N];
int find(int x)
{
	return lower_bound(nums.begin(),nums.end(),x)-nums.begin()+1; //返回的是从1开始的数
} 
signed main()
{
	FAST;
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		nums.push_back(a[i]);
	}
	sort(nums.begin(),nums.end());
	nums.erase(unique(nums.begin(),nums.end()),nums.end());
	for(int i=0;i<nums.size();i++){
		cout<<nums[i]<<" ";
	}
	return 0;
}

(3)线段树

1.朴素线段树

在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=1e6+7;
struct node{
	ll l;
	ll r;
	ll v;
	ll lazy;
}tree[N*4];
ll a[N]; 
void pushup(ll x){
	tree[x].v=tree[x*2].v+tree[x*2+1].v;
} 
void pushdown(ll x){
	tree[x*2].v+=tree[x].lazy*(tree[x*2].r-tree[x*2].l+1);
	tree[x*2+1].v+=tree[x].lazy*(tree[x*2+1].r-tree[x*2+1].l+1);
	tree[x*2].lazy+=tree[x].lazy;
	tree[x*2+1].lazy+=tree[x].lazy;
	tree[x].lazy=0;
}
void built(ll l,ll r,ll x){
	if(l==r){
		tree[x]={l,r,a[l]};
		return;
	}
	else tree[x]={l,r};
	ll mid=(l+r)/2;
	built(l,mid,x*2);
	built(mid+1,r,x*2+1);
	pushup(x); 
}
void modify(ll x,ll l,ll r,ll value){
	if(l<=tree[x].l&&tree[x].r<=r){
    		tree[x].v+=(tree[x].r-tree[x].l+1)*value;
    		tree[x].lazy+=value;
    		return;
    }
	pushdown(x);	
	ll mid=(tree[x].l+tree[x].r)/2;
	if(l<=mid)modify(x*2,l,r,value);
	if(r>mid)modify(x*2+1,l,r,value);
	pushup(x);
}
ll qurey(ll l,ll r,ll x){
	if(l<=tree[x].l&&tree[x].r<=r){
		return tree[x].v;
	}
	pushdown(x);
	ll mid=(tree[x].l+tree[x].r)/2;
	ll v=0;
	if(l<=mid)v+=qurey(l,r,x*2);
	if(r>mid)v+=qurey(l,r,x*2+1);
	return v;
}
int main(){
	ll n,m;
	cin>>n>>m;
	for(ll i=1;i<=n;i++){
		cin>>a[i];
	}
	built(1,n,1);
	for(ll i=1;i<=m;i++){
		char a;
		cin>>a;
		if(a=='Q'){
			ll x,y;
			cin>>x>>y; 
			cout<<qurey(x,y,1)<<endl;
		}
		else{
			ll x,y,z;
			cin>>x>>y>>z;
			modify(1,x,y,z);
		}
	}
	return 0;
}

2.进阶线段树

求区间连续字段和

在这里插入图片描述

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+7;
const int mi=INT_MIN;
int a[N];
struct node{
	int l;
	int r;
	int num;//区间内最大子串和 
	int lmax;//左边最大子串和 
	int rmax;//右边最大子串和 
	int sum;//区间内总和 
}tree[N*4];
void pushup(int x){
	tree[x].sum=tree[x<<1].sum+tree[x<<1|1].sum;
	tree[x].lmax=max(tree[x<<1].lmax,tree[x<<1].sum+tree[x<<1|1].lmax);
	tree[x].rmax=max(tree[x<<1|1].rmax,tree[x<<1|1].sum+tree[x<<1].rmax);
	tree[x].num=max(max(tree[x<<1].num,tree[x<<1|1].num),tree[x<<1].rmax+tree[x<<1|1].lmax);
}
void built(int l,int r,int x){
	if(l==r){
		tree[x]={l,r,a[l],a[l],a[l],a[l]};
		return;
	} 
	tree[x]={l,r,mi,mi,mi,mi};
	int mid=(l+r)>>1;
	built(l,mid,x<<1);
	built(mid+1,r,x<<1|1);
	pushup(x);
}
void modify(int p,int v,int x){
	if(tree[x].l==tree[x].r&&tree[x].l==p){
		tree[x].num=v;
		tree[x].lmax=v;
		tree[x].rmax=v;
		tree[x].sum=v;
		return;
	}
	int mid=(tree[x].l+tree[x].r)>>1;
	if(p<=mid)modify(p,v,x<<1);
	else if(p>mid)modify(p,v,x<<1|1);
	pushup(x);
}
node query(int l,int r,int x){
	if(tree[x].l>=l&&tree[x].r<=r){
		node xx=tree[x];
		return xx;
	}
	node no1={0,0,mi,mi,mi,0},no2={0,0,mi,mi,mi,0};
	int mid=(tree[x].l+tree[x].r)>>1;
	if(l<=mid)no1=query(l,r,x<<1);
	if(r>mid)no2=query(l,r,x<<1|1);
	node ans;
	ans.sum=no1.sum+no2.sum;
	ans.lmax=max(no1.lmax,no1.sum+no2.lmax);
	ans.rmax=max(no2.rmax,no2.sum+no1.rmax);
	ans.num=max(no1.rmax+no2.lmax,max(no1.num,no2.num));
	return ans;
}
signed main(){
	int n,q;
	cin>>n>>q;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	built(1,n,1);
	for(int i=1;i<=q;i++){
		int op,x,y;
		cin>>op>>x>>y;
		if(op==1){
			if(x>y)swap(x,y);
			node ans=query(x,y,1);
			cout<<ans.num<<endl;
		}
		else{
			modify(x,y,1);
		}
	}
	return 0;
}

线段树旋转

思路:线段树是一个满二叉树,所以,我们可以按照平衡树一样对每一个节点旋转

在这里插入图片描述

#include<bits/stdc++.h>
#define FAST ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define int long long
using namespace std;
const int N=1e6+7;
struct node{
	int l;
	int r;
	int v;
}tree[N*4];
int a[N],revers[N];
void pushup(int x){
	tree[x].v=tree[x<<1].v+tree[x<<1|1].v;
}
void built(int l,int r,int x){
	if(l==r){
		tree[x]={l,r,a[l]};
		return;
	}
	tree[x]={l,r,0};
	int mid=(l+r)/2;
	built(l,mid,x<<1);
	built(mid+1,r,x<<1|1);
	pushup(x);
}
void modify(int pos,int v,int x,int dep){
	if(tree[x].l==tree[x].r){
		tree[x].v=v;
		return;
	}
	int mid=(tree[x].l+tree[x].r)/2;
	int len=(tree[x].r-tree[x].l+1)/2;
	if(revers[dep]==0){
		if(pos<=mid)modify(pos,v,x<<1,dep-1);
		else modify(pos,v,x<<1|1,dep-1);
	}
	else{
		if(pos<=mid)modify(pos+len,v,x<<1|1,dep-1);
		else modify(pos-len,v,x<<1,dep-1);
	}
	pushup(x);
}
int query(int l,int r,int x,int dep){
	if(tree[x].l>=l&&tree[x].r<=r){
		return tree[x].v;
	}	
	int v=0;
	int mid=(tree[x].l+tree[x].r)/2;
	int len=(tree[x].r-tree[x].l+1)/2;
	if(revers[dep]==0){
		if(l<=mid)v+=query(l,r,x<<1,dep-1);
		if(r>mid)v+=query(l,r,x<<1|1,dep-1);
	}
	else{
		if(l<=mid)v+=query(l+len,r+len,x<<1|1,dep-1);
		if(r>mid)v+=query(l-len,r-len,x<<1,dep-1);//注意这边的变形 
	}
	return v;
}
signed main(){
	FAST;
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=(1<<n);i++){
		cin>>a[i];
	}
	built(1,(1<<n),1);
	for(int i=1;i<=m;i++){
		int op;
		cin>>op;
		if(op==1){
			int x,k;
			cin>>x>>k;
			modify(x,k,1,n);
		} 
		else if(op==3){//交换区间 
			int v;
			cin>>v;
			revers[v+1]^=1;//交换区间的父亲节点
		}
		else if(op==2){//区间翻转 
			int v;
			cin>>v;
			for(int i=1;i<=v;i++)revers[i]^=1;
		} 
		else{
			int l,r;
			cin>>l>>r;
			cout<<query(l,r,1,n)<<"\n";
		}
	}
	return 0;
}

求区间平方和,立方和

在这里插入图片描述

思路:简单的来说就是区间覆盖区间加,区间乘,区间求一次方,二次方,三次方和

#include<bits/stdc++.h>
#define FAST ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define int long long
using namespace std;
const int N=1e6+7;
int a[N];
const int mod=10007;
struct node{
	int l;
	int r;
	int v1;
	int v2;
	int v3;
	int lazyadd;
	int lazymul;
	int lazycha;
}tree[N*4];
void built(int l,int r,int x){
	tree[x]={l,r,0,0,0,0,1,0};
	if(l==r){
		return;	
	}
	int mid=(l+r)/2;
	built(l,mid,x<<1);
	built(mid+1,r,x<<1|1);
}
void pushup(int x){
	tree[x].v1=(tree[x<<1].v1%mod+tree[x<<1|1].v1%mod)%mod;
	tree[x].v2=(tree[x<<1].v2%mod+tree[x<<1|1].v2%mod)%mod;
	tree[x].v3=(tree[x<<1].v3%mod+tree[x<<1|1].v3%mod)%mod;
}
void pushdown(int x){
	if(tree[x].lazycha){//首先是修改懒标记 
		int v=tree[x].lazycha%mod;
		tree[x].lazycha=0;
		tree[x<<1].v3=(tree[x<<1].r-tree[x<<1].l+1)%mod*v%mod*v%mod*v%mod;
		tree[x<<1].v2=(tree[x<<1].r-tree[x<<1].l+1)%mod*v%mod*v%mod;	
		tree[x<<1].v1=(tree[x<<1].r-tree[x<<1].l+1)%mod*v%mod;
		tree[x<<1|1].v3=(tree[x<<1|1].r-tree[x<<1|1].l+1)%mod*v%mod*v%mod*v%mod;
		tree[x<<1|1].v2=(tree[x<<1|1].r-tree[x<<1|1].l+1)%mod*v%mod*v%mod;	
		tree[x<<1|1].v1=(tree[x<<1|1].r-tree[x<<1|1].l+1)%mod*v%mod;
		tree[x<<1].lazycha=v%mod;
		tree[x<<1|1].lazycha=v%mod;
		tree[x<<1].lazyadd=0;
		tree[x<<1|1].lazyadd=0;
		tree[x<<1].lazymul=1;
		tree[x<<1|1].lazymul=1;
	}
	if(tree[x].lazymul!=1){//然后是乘法懒标记 
		int v=tree[x].lazymul%mod;
		tree[x].lazymul=1;
		tree[x<<1].lazymul=(tree[x<<1].lazymul%mod*v%mod)%mod;
		tree[x<<1|1].lazymul=(tree[x<<1|1].lazymul%mod*v%mod)%mod;
		tree[x<<1].lazyadd=(tree[x<<1].lazyadd%mod*v%mod)%mod;
		tree[x<<1|1].lazyadd=(tree[x<<1|1].lazyadd%mod*v%mod)%mod;
		tree[x<<1].v3=(tree[x<<1].v3%mod*v%mod*v%mod*v%mod)%mod;
		tree[x<<1].v2=(tree[x<<1].v2%mod*v%mod*v%mod)%mod;	
		tree[x<<1].v1=(tree[x<<1].v1%mod*v%mod)%mod;	
		tree[x<<1|1].v3=(tree[x<<1|1].v3%mod*v%mod*v%mod*v%mod)%mod;
		tree[x<<1|1].v2=(tree[x<<1|1].v2%mod*v%mod*v%mod)%mod;	
		tree[x<<1|1].v1=(tree[x<<1|1].v1%mod*v%mod)%mod;	
	}
	if(tree[x].lazyadd){//最后 
		int v=tree[x].lazyadd;
		tree[x].lazyadd=0;
	tree[x<<1].v3=(tree[x<<1].v3%mod+3*v%mod*tree[x<<1].v2%mod+3*v%mod*v%mod*tree[x<<1].v1%mod+(tree[x<<1].r-tree[x<<1].l+1)%mod*v%mod*v%mod*v%mod)%mod;
	tree[x<<1].v2=(tree[x<<1].v2%mod+2*v%mod*tree[x<<1].v1%mod+(tree[x<<1].r-tree[x<<1].l+1)*v%mod*v%mod)%mod;	
	tree[x<<1].v1=(tree[x<<1].v1%mod+(tree[x<<1].r-tree[x<<1].l+1)%mod*v%mod)%mod;
	tree[x<<1|1].v3=(tree[x<<1|1].v3%mod+3*v%mod*tree[x<<1|1].v2%mod+3*v%mod*v%mod*tree[x<<1|1].v1%mod+(tree[x<<1|1].r-tree[x<<1|1].l+1)%mod*v%mod*v%mod*v%mod)%mod;
	tree[x<<1|1].v2=(tree[x<<1|1].v2%mod+2*v%mod*tree[x<<1|1].v1%mod+(tree[x<<1|1].r-tree[x<<1|1].l+1)*v%mod*v%mod)%mod;	
	tree[x<<1|1].v1=(tree[x<<1|1].v1%mod+(tree[x<<1|1].r-tree[x<<1|1].l+1)%mod*v%mod)%mod;	
		tree[x<<1].lazyadd=(tree[x<<1].lazyadd%mod+v%mod)%mod;	
		tree[x<<1|1].lazyadd=(tree[x<<1|1].lazyadd%mod+v%mod)%mod;
	}
}
void modify1(int l,int r,int v,int x){//加法懒标记 
	if(tree[x].l>=l&&tree[x].r<=r){
	tree[x].v3=(tree[x].v3%mod+3*v%mod*tree[x].v2%mod+3*v%mod*v%mod*tree[x].v1%mod+(tree[x].r-tree[x].l+1)%mod*v%mod*v%mod*v%mod)%mod;
		tree[x].v2=(tree[x].v2%mod+2*v%mod*tree[x].v1%mod+(tree[x].r-tree[x].l+1)*v%mod*v%mod)%mod;	
		tree[x].v1=(tree[x].v1%mod+(tree[x].r-tree[x].l+1)%mod*v%mod)%mod;	
		tree[x].lazyadd=(tree[x].lazyadd%mod+v%mod)%mod;
		return;
	}
	pushdown(x);
	int mid=(tree[x].l+tree[x].r)/2;
	if(l<=mid)modify1(l,r,v,x<<1);
	if(r>mid)modify1(l,r,v,x<<1|1);
	pushup(x);
}
void modify2(int l,int r,int v,int x){//乘法懒标记 
	if(tree[x].l>=l&&tree[x].r<=r){
		tree[x].v3=(tree[x].v3%mod*v%mod*v%mod*v%mod)%mod;
		tree[x].v2=(tree[x].v2%mod*v%mod*v%mod)%mod;	
		tree[x].v1=(tree[x].v1%mod*v%mod)%mod;	
		tree[x].lazymul=(tree[x].lazymul%mod*v%mod)%mod;
		tree[x].lazyadd=(tree[x].lazyadd%mod*v%mod)%mod;
		return;
	}
	pushdown(x);
	int mid=(tree[x].l+tree[x].r)/2;
	if(l<=mid)modify2(l,r,v,x<<1);
	if(r>mid)modify2(l,r,v,x<<1|1);
	pushup(x);
}
void modify3(int l,int r,int v,int x){//替换懒标记 
	if(tree[x].l>=l&&tree[x].r<=r){
		tree[x].v3=(tree[x].r-tree[x].l+1)%mod*v%mod*v%mod*v%mod;
		tree[x].v2=(tree[x].r-tree[x].l+1)%mod*v%mod*v%mod;	
		tree[x].v1=(tree[x].r-tree[x].l+1)%mod*v%mod;	
		tree[x].lazycha=v%mod;
		tree[x].lazyadd=0;
		tree[x].lazymul=1;
		return;
	}
	pushdown(x);
	int mid=(tree[x].l+tree[x].r)/2;
	if(l<=mid)modify3(l,r,v,x<<1);
	if(r>mid)modify3(l,r,v,x<<1|1);
	pushup(x);
}
int query(int l,int r,int p,int x){
	if(tree[x].l>=l&&tree[x].r<=r){
		if(p==1)return tree[x].v1%mod;
		else if(p==2)return tree[x].v2%mod;
		else return tree[x].v3%mod; 
	}
	int v=0;
	pushdown(x);
	int mid=(tree[x].l+tree[x].r)/2;
	if(l<=mid)v=(v%mod+query(l,r,p,x<<1))%mod;
	if(r>mid)v=(v%mod+query(l,r,p,x<<1|1))%mod;
	pushup(x);
	return v%mod;
}
signed main(){
	FAST;
	int n,m;
	while(1){
	cin>>n>>m;
		if(n==0&&m==0)return 0;
		built(1,n,1);
		for(int i=1;i<=m;i++){
			int op,l,r,x;
			cin>>op>>l>>r>>x;
			if(op==1){
				modify1(l,r,x%mod,1);
			}
			else if(op==2){
				modify2(l,r,x%mod,1); 
			}
			else if(op==3){
				modify3(l,r,x%mod,1);
			}
			else{
				cout<<query(l,r,x,1)%mod<<"\n";
			}
		} 
	}
	return 0;
}

线段树万金油

在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
const long long N=2e6+7;
long long a[N];
long long b[N];
long long c;
struct node{
	long long l;
	long long r;
	long long maxn;
}tree[4*N];
void pushup(long long x){
	tree[x].maxn=max(tree[2*x].maxn,tree[2*x+1].maxn);
}
void built(long long l,long long r,long long x){
    tree[x]={l,r,c};
	if(l==r){
    	return;    
    }
    else{
        long long mid=(l+r)/2;
        built(l,mid,x*2);
        built(mid+1,r,x*2+1);
    }
    pushup(x);
}
void modify(long long p,long long x,long long y){//p表示值,x表示当前节点,y表示添加第几个数 
	if(tree[x].l==y&&tree[x].r==y){
		tree[x].maxn=p;
		return;
	}
	long long mid=(tree[x].l+tree[x].r)/2;
	if(y<=mid){
		modify(p,x*2,y);
	}
	else {
		modify(p,x*2+1,y);
	}
	pushup(x);
}
long long query(long long x,long long v){
	if(tree[x].l==tree[x].r){
		return tree[x].l;
	}
	if(tree[x*2].maxn>=v){
		return query(x*2,v); 
	}
	else if(tree[x*2+1].maxn>=v){
		return query(x*2+1,v);
	}
}
int main(){
	long long t;
	scanf("%lld",&t);
	while(t--){
		map<long long,long long>mapp;
		long long n;
		scanf("%lld%lld",&n,&c);
		long long cnt2=0,cnt1=0;
		for(long long i=1;i<=n;i++){
			scanf("%lld",&a[i]);
			b[i]=c;
		}
		built(1,n,1);
		cnt1=0;
		for(long long i=1;i<=n;i++){//能往前面填就往前面填
			long long check=query(1,a[i]);
			long long xx=b[check]-a[i];
			modify(xx,1,check);
			b[check]=b[check]-a[i];
			cnt1=max(cnt1,check);
		}
		mapp[c] = 1;
		for(long long i=1;i<=n;i++){//能恰好才填
			map<long long,long long>::iterator iter=mapp.lower_bound(a[i]);
			if(mapp.end()==iter){
				mapp[c-a[i]]++;
			}
			else{
				mapp[iter->first-a[i]]++;
				mapp[iter->first]--;
				if(mapp[iter->first]==0)mapp.erase(iter);
			}
		}
		for(auto x:mapp)cnt2+=x.second;
		printf("%lld %lld\n",cnt1,cnt2);
	}
	return 0;
} 

3.线段树二分

题目:HNCPC Multi-university Training Round #9

题目大意:商店里面有n个物品,价格分别为di,有m个人依次进入商店,每一个人购买物品的时候,只要钱带够了,就买。问每一个人能卖什么物品。

思路:线段树维护最大值,然后二分查找。钱大于等于左子树物品的最大值,就往左边递归;否则,若钱大于等于右子树物品的最大值,往右递归;若都小于,则不能购买。

#include<bits/stdc++.h>
using namespace std;
const long long N=1e6+7;
long long a[N];
long long b[N];
vector<long long>q[N];
struct node{
	long long l;
	long long r;
	long long maxn;
}tree[4*N];
void pushup(long long x){
	tree[x].maxn=max(tree[2*x].maxn,tree[2*x+1].maxn);
}
void built(long long l,long long r,long long x){
	if(l==r){
		tree[x]={l,r,b[l]};
    	return;    
    }
    else{
    	tree[x]={l,r,0};
        long long mid=(l+r)/2;
        built(l,mid,x*2);
        built(mid+1,r,x*2+1);
    }
    pushup(x);
}
void modify(long long p,long long x,long long y){//p表示值,x表示当前节点,y表示添加第几个数 
	if(tree[x].l==y&&tree[x].r==y){
		tree[x].maxn=p;
		return;
	}
	long long mid=(tree[x].l+tree[x].r)/2;
	if(y<=mid){
		modify(p,x*2,y);
	}
	else {
		modify(p,x*2+1,y);
	}
	pushup(x);
}
long long query(long long x,long long v){
	if(tree[x].l==tree[x].r){
		return tree[x].l;
	}
	if(tree[x*2].maxn>=v){
		return query(x*2,v); 
	}
	else if(tree[x*2+1].maxn>=v){
		return query(x*2+1,v);
	}
	else return -1;
}
int main(){
	ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
	long long n;
	cin>>n;
	for(long long i=1;i<=n;i++){
		cin>>a[i];
	}
	long long m;
	cin>>m;
	for(long long i=1;i<=m;i++){
		cin>>b[i];
	}
	built(1,m,1);
	for(long long i=1;i<=n;i++){
		long long check=query(1,a[i]);
		if(check==-1)continue;
		long long xx=b[check]-a[i];
		modify(xx,1,check);
		b[check]=b[check]-a[i];
		q[check].push_back(i);
	}
	for(long long i=1;i<=m;i++){
		long long len=q[i].size();
		cout<<len<<" ";
		for(long long j=0;j<len;j++){
			cout<<q[i][j]<<" ";
		}
		cout<<endl;
	}
	return 0;
}

4.势能线段树

当我们需要区间修改时候却不能够用lazy去维护的时候,我们就需要用势能线段树去暴力的修改每一个点。势能线段树的关键在与能够更好的剪枝。

在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
const long long N=1e6+1e4;
long long a[N];
long long num[N],d[N];//num存约数个数,d存一个数最小质因子的个数
bool vis[N];
long long prime[N];
struct node{
	long long l;
	long long r;
	long long sum;
	long long maxn;
}tree[N*4];
template<class t>inline void read( t &res){
	char c;t flag=1;
	while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
	while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;	
}
void fun(long long n)//线性筛求约数个数
{
	memset(vis,0,sizeof(vis));
	num[1]=1;
	d[1]=1;
	prime[0]=0;
	for(long long i=2;i<=n;i++)
	{
		if(!vis[i])
		{
			prime[++prime[0]]=i;
			d[i]=1;
			num[i]=2;
		}
		for(long long j=1;j<=prime[0]&&i<=n/prime[j];j++)
		{
			vis[i*prime[j]]=1;
			if(i%prime[j]==0)
			{
				d[i*prime[j]]=d[i]+1;
				num[i*prime[j]]=num[i]/(d[i]+1)*(d[i]+2);
				break;
			}
			d[i*prime[j]]=1;
			num[i*prime[j]]=num[i]*2;
		}
	}
}
void pushup(long long x){
	tree[x].sum=tree[x<<1].sum+tree[x<<1|1].sum;
	tree[x].maxn=max(tree[x<<1].maxn,tree[x<<1|1].maxn);
}
void built(long long l,long long r,long long x){
	if(l==r){
		tree[x]={l,r,a[l],a[l]};
		return;
	}
	tree[x]={l,r,0,0};
	long long mid=(l+r)>>1;
	built(l,mid,x<<1);
	built(mid+1,r,x<<1|1);
	pushup(x);
}
void modify(long long l,long long r,long long x){
	if(tree[x].maxn<3)return;//若一个小于等于2的时候,那么这个数的约数也等于这个数。
	if(tree[x].l==tree[x].r){
		tree[x].sum=num[tree[x].sum];
		tree[x].maxn=tree[x].sum;
		return;
	}
	long long mid=(tree[x].l+tree[x].r)>>1;
	if(l<=mid)modify(l,r,x<<1);
	if(r>mid)modify(l,r,x<<1|1);
	pushup(x);
}
long long query(long long l,long long r,long long x){
	if(tree[x].l>=l&&tree[x].r<=r){
		return tree[x].sum;
	}
	long long v=0;
	long long mid=(tree[x].l+tree[x].r)>>1;
	if(l<=mid)v+=query(l,r,x<<1);
	if(r>mid)v+=query(l,r,x<<1|1);
	return v;
}
int main(){
	long long n,m;
	read(n);
	read(m);
	fun(1e6+2);
	for(long long i=1;i<=n;i++)read(a[i]);
	built(1,n,1); 
	for(long long i=1;i<=m;i++){
		long long x,y,z;
		read(x),read(y),read(z);
		if(x==1){
			modify(y,z,1);
		}
		else{
			printf("%lld\n",query(y,z,1));
		}
	}
	return 0;
}

5.动态开点线段树(在字符串上的应用)

在这里插入图片描述

思路:我们可以开26个动态开点线段树来存储字符串,然后统计区间内的各个元素的个数,然后区间覆盖

#include<bits/stdc++.h>
#define FAST ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define int long long
using namespace std;
const int N=1e5+7;
char a[N];
struct node{
	int l;
	int r;
	int lazy;
	int v;
}tree[N*80];//建立26棵线段树 
int vis[30];
int dfn,root[30];//26颗线段树所在的点 
int n,m;
void pushup(int x){
	tree[x].v=tree[tree[x].l].v+tree[tree[x].r].v;
}
void pushdown(int x,int l,int r){
	if(tree[x].lazy!=-1){
		int mid=(l+r)/2;
		if(!tree[x].l)tree[x].l=++dfn;
		if(!tree[x].r)tree[x].r=++dfn;//一定注意这里 
		tree[tree[x].l].v=tree[x].lazy*(mid-l+1);
		tree[tree[x].r].v=tree[x].lazy*(r-mid);
		tree[tree[x].l].lazy=tree[x].lazy;
		tree[tree[x].r].lazy=tree[x].lazy;
		tree[x].lazy=-1;
	}
}
void insert(int &x,int l,int r,int v,int L,int R){
	if(!x){
		x=++dfn;
		tree[x].lazy=-1;
	}
	if(L>=l&&R<=r){
		tree[x].v=v*(R-L+1);
		tree[x].lazy=v; 
		return;
	}
	pushdown(x,L,R);
	int mid=(L+R)/2;
	if(l<=mid)insert(tree[x].l,l,r,v,L,mid);
	if(r>mid)insert(tree[x].r,l,r,v,mid+1,R);
	pushup(x); 
}
int query(int x,int l,int r,int L,int R){
	if(!x)return 0;
	int v=0;
	if(L>=l&&R<=r){
		return tree[x].v;
	}
	pushdown(x,L,R);
	int mid=(L+R)/2;
	if(l<=mid)v+=query(tree[x].l,l,r,L,mid);
	if(r>mid)v+=query(tree[x].r,l,r,mid+1,R);
	return v;
}
signed main(){
	FAST;
	cin>>n>>m;
	cin>>a;
	for(int i=0;i<n;i++){
		insert(root[(int)(a[i]-'a'+1)],i+1,i+1,1,1,n);
	}
	int l,r,op;
	for(int i=1;i<=m;i++){
		cin>>l>>r>>op;
		if(op==1){
			for(int i=1;i<=26;i++){
				vis[i]=query(root[i],l,r,1,n);
				if(vis[i])insert(root[i],l,r,0,1,n);
			}
			int be=l;
			for(int i=1;i<=26;i++){
				if(vis[i]){
					insert(root[i],be,be+vis[i]-1,1,1,n);
					be=be+vis[i];
				}
			}
		}
		else{
			for(int i=1;i<=26;i++){
				vis[i]=query(root[i],l,r,1,n);
				if(vis[i])insert(root[i],l,r,0,1,n);
			}
			int be=l;
			for(int i=26;i>=1;i--){
				if(vis[i]){
					insert(root[i],be,be+vis[i]-1,1,1,n);
					be=be+vis[i];
				}
			}	
		}
	}		
	for(int i=1;i<=n;i++){
		for(int j=1;j<=26;j++){
			if(query(root[j],i,i,1,n)){
				cout<<(char)(j+'a'-1);
				break;
			}
		}
	}
	return 0;
}

6.线段树合并

朴素线段树合并

题目大意:

建立一棵树,对于一颗树来说,输出每个以该节点为根的所有点所组成的有多少个连续区间。

#include<bits/stdc++.h>
#define FAST ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define int long long
using namespace std;
const int N=2e5+7;
struct node{
	int l;
	int r;
	int vl;
	int vr;
	int cnt;
}tree[N*80];
int n;
vector<int>q[N];
int ans[N],root[N],dfn;
void pushup(int x){
	tree[x].cnt=tree[tree[x].l].cnt+tree[tree[x].r].cnt;
	tree[x].vl=tree[tree[x].l].vl;
	tree[x].vr=tree[tree[x].r].vr;   
	if(tree[tree[x].r].vl&&tree[tree[x].l].vr)tree[x].cnt--;
}
void insert(int &x,int pos,int L=1,int R=n){
	if(!x){
		x=++dfn;	
	}
	if(L==R){
		tree[x].cnt=1;
		tree[x].vl=1;
		tree[x].vr=1;
		return;
	}
	int mid=(L+R)>>1;
	if(pos<=mid)insert(tree[x].l,pos,L,mid);
	else insert(tree[x].r,pos,mid+1,R);
	pushup(x);
}
int merge(int px,int py,int L=1,int R=n){
	if(!px)return py;
	if(!py)return px;
	if(L==R){
		//tree[px].cnt=tree[px].vl=tree[px].vr=1;
		return px;
	}
	int mid=(L+R)/2;
	tree[px].l=merge(tree[px].l,tree[py].l,L,mid);
	tree[px].r=merge(tree[px].r,tree[py].r,mid+1,R);
	pushup(px);
	return px; 
}
void dfs(int x,int fa){
	insert(root[x],x);
	for(int i=0;i<q[x].size();i++){
		if(q[x][i]!=fa){
			dfs(q[x][i],x);
			root[x]=merge(root[x],root[q[x][i]]);
		}
	}
	ans[x]=tree[root[x]].cnt;
}
signed main(){
	FAST; 
	int t;
	cin>>t;
	int k=0;
	while(t--){
		k++;
		cout<<"Case #"<<k<<": ";
		cin>>n;
		for (int i = 1; i <= n; i ++ ) ans[i] = root[i] = 0;
		for(int i=1;i<=dfn;i++)tree[i] = {0, 0, 0, 0, 0};
		dfn=0;
		int x,y;
		for(int i=1;i<n;i++){
			cin>>x>>y;
			q[x].push_back(y);
			q[y].push_back(x);
		}
		dfs(1,0);
		for(int i=1;i<=n;i++){
			if(i==n)cout<<ans[i]<<"\n";
			else cout<<ans[i]<<" ";
			q[i].clear();
		}
    }
} 

线段树合并的综合应用

在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rRwhLo4g-1687510783092)(ACM模板.assets/image-20230530082553048.png)]

思路:本题的主要采用的算法是树上差分+线段树合并

#include<bits/stdc++.h>
#define FAST ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define int long long
using namespace std;
const int N=1e6+7;
vector<int>q[N];
struct node{
	int l;
	int r;
	int v;	 
	int maxn;
}tree[N*8]; 
int sizes[N],f[N],son[N],d[N],top[N];
void dfs1(int x,int fa){
	sizes[x]=1,son[x]=0,d[x]=d[fa]+1,f[x]=fa;
	for(int i=0;i<q[x].size();i++){
		if(q[x][i]!=fa){
			dfs1(q[x][i],x);
			sizes[x]+=sizes[q[x][i]];
			if(sizes[son[x]]<sizes[q[x][i]])son[x]=q[x][i];
		}
	}
}
void dfs2(int x,int topx){
	top[x]=topx;
	if(son[x])dfs2(son[x],topx);
	for(int i=0;i<q[x].size();i++){
		if(q[x][i]!=f[x]&&q[x][i]!=son[x])
		dfs2(q[x][i],q[x][i]); 
	}
}
int chain(int x,int y){
	while(top[x]!=top[y]){
		if(d[top[x]]<d[top[y]])swap(x,y);
		x=f[top[x]];
	}
	return d[x]<d[y]?x:y;
}
void pushup(int x){
	tree[x].v=tree[tree[x].l].v+tree[tree[x].r].v;
	tree[x].maxn=max(tree[tree[x].l].maxn,tree[tree[x].r].maxn);
}
int root[N],dfn;
void insert(int &x,int pos,int v,int L=0,int R=1e5+7){
	if(!x)x=++dfn;
	if(L==R){
		tree[x].v+=v;
		tree[x].maxn=tree[x].v;
		return;
	}
	int mid=(L+R)>>1;
	if(pos<=mid)insert(tree[x].l,pos,v,L,mid);
	else insert(tree[x].r,pos,v,mid+1,R);
	pushup(x);
}
int merge(int px,int py,int L=0,int R=1e5+7){
	if(!px)return py;
	if(!py)return px;
	if(L==R){
		tree[px].v=tree[px].v+tree[py].v;
		tree[px].maxn=tree[px].v;
		return px;
	}
	int mid=(L+R)/2;
	tree[px].l=merge(tree[px].l,tree[py].l,L,mid);
	tree[px].r=merge(tree[px].r,tree[py].r,mid+1,R);
	pushup(px);
	return px;
}
int query(int x,int L=0,int R=1e5+7){
	if(!x)x=++dfn;
	if(L==R){
		return L;
	}
	int mid=(L+R)>>1;
	if(tree[tree[x].l].maxn>=tree[tree[x].r].maxn)return query(tree[x].l,L,mid);
	else return query(tree[x].r,mid+1,R);
}
int anser[N];
void dfs(int x){
	for(int i=0;i<q[x].size();i++){
		if(q[x][i]!=f[x]){
			dfs(q[x][i]);
			root[x]=merge(root[x],root[q[x][i]]);
		}
	}
	anser[x]=query(root[x]);
}
signed main(){
	FAST;
	int n,m;
	cin>>n>>m;
	for(int i=1;i<n;i++){
		int x,y;
		cin>>x>>y;
		q[x].push_back(y);
		q[y].push_back(x);
	}
	dfs1(1,0);
	dfs2(1,1);
	for(int i=1;i<=m;i++){
		int x,y,z;
		cin>>x>>y>>z;
		insert(root[x],z,1);
		insert(root[y],z,1);
		int xx=chain(x,y);
		insert(root[xx],z,-1);
		insert(root[f[xx]],z,-1);
	}
	dfs(1);
	for(int i=1;i<=n;i++){
		cout<<anser[i]<<"\n";
	}
	return 0;
}

7.线段树分裂

#include<bits/stdc++.h>
#define FAST ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define int long long
using namespace std;
const int N=1e6+7;
int root[N];
struct node{
	int l;
	int r;
	int v;
}tree[N*8];
int dfn;
void pushup(int x){
	tree[x].v=tree[tree[x].l].v+tree[tree[x].r].v;
}
//void spilt(int px,int &py,int k){	
//	if(!py)py=++dfn;
//	int v=tree[tree[px].l].v;
//	if(k>v)spilt(tree[px].r,tree[py].r,k-v);
//	else swap(tree[px].r,tree[py].r);
//	if(k<v)spilt(tree[px].l,tree[py].l,k);
//	tree[py].v=tree[px].v-k;
//	tree[px].v=k;
//}
// 将 p 树上区间 l~r的数据,全部转移到 q 树上
void split(int px,int &py,int l,int r,int L=0,int R=2e5+7) {
	if (!px) return; // 原树上没有数据了,直接返回
	py=++dfn; // 建立一个新树的节点
	if (L>=l&&R<=r) {
		// 刚好囊括区间,直接将这个区间给到新树上
		swap(tree[px],tree[py]);
		return;
	}
	int mid=(L+R)>>1;
	// 左右递归分裂
	if (l<=mid) split(tree[px].l,tree[py].l,l,r,L,mid);
	if (r>mid) split(tree[px].r,tree[py].r,l,r,mid+1,R);
	// 跟新分裂后的节点信息
	pushup(px);
	pushup(py);
}
int merge(int px,int py,int L=0,int R=2e5+7){
	if(!px)return py;
	if(!py)return px;
	if(L==R){
		tree[px].v+=tree[py].v;
		return px;
	}
	int mid=(L+R)/2;
	tree[px].l=merge(tree[px].l,tree[py].l,L,mid);
	tree[px].r=merge(tree[px].r,tree[py].r,mid+1,R);
	pushup(px);
	return px; 
}
void insert(int &x,int pos,int v,int L=0,int R=2e5+7){
	if(!x)x=++dfn;
	if(L==R){
		tree[x].v+=v;
		return;
	}
	int mid=(L+R)/2;
	if(pos<=mid)insert(tree[x].l,pos,v,L,mid);
	else insert(tree[x].r,pos,v,mid+1,R);
	pushup(x); 
} 
int query(int x,int l,int r,int L=0,int R=2e5+7){
	if(!x)return 0;
	if(L>=l&&R<=r){
		return tree[x].v;
	}
	int v=0;
	int mid=(L+R)/2;
	if(l<=mid)v+=query(tree[x].l,l,r,L,mid);
	if(r>mid)v+=query(tree[x].r,l,r,mid+1,R);
	return v;
}
int k(int x,int v,int L=0,int R=2e5+7){
	if(tree[x].v<v)return -1;
	if(L==R){
		return L;
	}
	int mid=(L+R)/2;
	if(tree[tree[x].l].v>=v){
		return k(tree[x].l,v,L,mid);
	}
	else if(tree[tree[x].l].v<v){
		return k(tree[x].r,v-tree[tree[x].l].v,mid+1,R);
	}
}
signed main(){
	FAST;
	int n,m;
	cin>>n>>m;
	int xx;
	for(int i=1;i<=n;i++){
		cin>>xx;
		insert(root[1],i,xx);
	}
	int op,x,y,z;int pre=2;
	for(int i=1;i<=m;i++){
		cin>>op;
		if(op==0){//线段树分裂
			cin>>x>>y>>z;
			split(root[x],root[pre],y,z);
			pre++;
		} 
		else if(op==1){//线段树合并
			cin>>x>>y;
			root[x]=merge(root[x],root[y]);
		}
		else if(op==2){	
			cin>>x>>y>>z;
			insert(root[x],z,y);
		}
		else if(op==3){
			cin>>x>>y>>z;
			int xx=query(root[x],1,z);
			int yy=query(root[x],1,y-1);
			cout<<xx-yy<<"\n"; 
		}
		else{
			cin>>x>>y;
			int xx=k(root[x],y);
			cout<<xx<<"\n";
		} 
	}
	return 0;
}

8.二维数点问题

在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+7;
int a[N];
inline int read(){
    register int x = 0, t = 1;
    register char ch=getchar(); // 读入单个字符到寄存器
    while(ch<'0'||ch>'9'){
        if(ch=='-')
            t=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);  // 移位与异或
      	// 第十行可以换成 x = x * 10 + ch - '0'
        ch=getchar();
    }
    return x*t;
}
inline void write(int x)
{
    if(x<0){
    	putchar('-');
		x=-x;
	}
    if(x>9) 
		write(x/10);
    putchar(x%10+'0');
}
struct node{
	int l;
	int r;
	int v;
	int lazy;
}tree[N*4];
void built(int x,int l,int r){
	tree[x]={l,r,0,0};
	if(l==r){
		return;	
	}
	int mid=(l+r)>>1;
	built(x<<1,l,mid);
	built(x<<1|1,mid+1,r);
}
void pushup(int x){
	tree[x].v=tree[x<<1].v+tree[x<<1|1].v;
}
void pushdown(int x){
	if(tree[x].lazy){
		tree[x<<1].v+=(tree[x<<1].r-tree[x<<1].l+1)*tree[x].v;
		tree[x<<1|1].v+=(tree[x<<1|1].r-tree[x<<1|1].l+1)*tree[x].v;
		tree[x<<1].lazy+=tree[x].lazy;
		tree[x<<1|1].lazy+=tree[x].lazy;
		tree[x].lazy=0;
		return;
	}
}
void modify(int x,int l,int r,int v){
	if(tree[x].l>=l&&tree[x].r<=r){
		tree[x].v+=(tree[x].r-tree[x].l+1)*v;
		tree[x].lazy+=v;
		return;
	}
	pushdown(x);
	int mid=(tree[x].l+tree[x].r)>>1;
	if(l<=mid)modify(x<<1,l,r,v);
	if(r>mid)modify(x<<1|1,l,r,v);
	pushup(x);
}
int query(int x,int l,int r){
	if(tree[x].l>=l&&tree[x].r<=r){
		return tree[x].v;
	}
	int v=0;
	pushdown(x);
	int mid=(tree[x].l+tree[x].r)>>1;
	if(l<=mid)v+=query(x<<1,l,r);
	if(r>mid)v+=query(x<<1|1,l,r);
	return v; 
}
struct nod{
	int x;
	int l,r;
	int f;
	int pos;
}b[N*2];
bool cmp(const nod xx,const nod yy){
	return xx.x<yy.x;
}
int ans[N];
signed main(){
	int n,m;
	n=read();
	m=read();
	int maxn=0;
	for(int i=1;i<=n;i++){
		a[i]=read();
		maxn=max(maxn,a[i]);
	}
	int cnt=0;
	int xx,yy,zz,ss;
	for(int i=1;i<=m;i++){
		xx=read();
		yy=read();
		zz=read();
		ss=read();
		b[++cnt].x=xx-1,b[cnt].l=zz,b[cnt].r=ss,b[cnt].f=-1,b[cnt].pos=i;
		b[++cnt].x=yy,b[cnt].l=zz,b[cnt].r=ss,b[cnt].f=1,b[cnt].pos=i;
	}//将一段区间差分为两段区间
	sort(b+1,b+1+2*m,cmp);
	built(1,1,maxn);
	int pos=1;
	for(int i=1;i<=2*m;i++){
		while(pos<=b[i].x){
			modify(1,a[pos],a[pos],1); 
			pos++;
		}
		ans[b[i].pos]+=(b[i].f*(query(1,b[i].l,b[i].r)));
	}
	for(int i=1;i<=m;i++){
		write(ans[i]);
		putchar('\n');
	}
}

在这里插入图片描述

#include<bits/stdc++.h>
#define FAST ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define int long long
using namespace std;
const int N=1e6+7;
int a[N];
struct node{
	int l;
	int r;
	int pos;
}b[N];
bool cmp(const node xx,const node yy){
	return xx.r<yy.r;
}
struct nod{
	int l;
	int r;
	int v;
}tree[N*4];
int pre[N];
void pushup(int x){
	tree[x].v=tree[x<<1].v+tree[x<<1|1].v;
}
void built(int x,int l,int r){
	tree[x]={l,r,0}; 
	if(l==r){
		return;
	}
	int mid=(l+r)>>1;
	built(x<<1,l,mid);
	built(x<<1|1,mid+1,r);
}
void modify(int x,int pos,int v){
	if(tree[x].l==tree[x].r){
		tree[x].v+=v;
		return;
	}
	int mid=(tree[x].l+tree[x].r)>>1;
	if(pos<=mid)modify(x<<1,pos,v);
	else modify(x<<1|1,pos,v);
	pushup(x); 
}
int query(int x,int l,int r){
	if(tree[x].l>=l&&tree[x].r<=r){
		return tree[x].v;
	}
	int v=0;
	int mid=(tree[x].l+tree[x].r)>>1;
	if(l<=mid)v+=query(x<<1,l,r);
	if(r>mid)v+=query(x<<1|1,l,r);
	return v;
}
int ans[N];
signed main(){
	FAST;
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	} 
	int q;
	cin>>q;
	for(int i=1;i<=q;i++){
		cin>>b[i].l>>b[i].r;
		b[i].pos=i;
	}
	sort(b+1,b+1+q,cmp);
	int pos=1;
	built(1,1,n);
	for(int i=1;i<=q;i++){
		while(pos<=b[i].r){
			if(!pre[a[pos]]){
				modify(1,pos,1);
				pre[a[pos]]=pos;
			}
			else{
				modify(1,pre[a[pos]],-1);
				modify(1,pos,1);
				pre[a[pos]]=pos;
			}
			pos++;
		}
		ans[b[i].pos]=query(1,b[i].l,b[i].r);
	}
	for(int i=1;i<=q;i++){
		cout<<ans[i]<<"\n";
	}
	return 0;
}

(4)珂朵莉树

#include<bits/stdc++.h>
#define FAST ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define int long long
using namespace std;
const int N=1e6+7;
struct node{
	int l,r;
	mutable int val;
	bool operator<(const node &a)const{return l<a.l;}
	node (int L,int R,int Val):l(L),r(R),val(Val){}//构造函数,赋值
	node (int L):l(L){}//构造函数,询问区间
};
set<node>s;
using  si = set<node>::iterator;
si spilt(int pos){//分裂
	auto it=s.lower_bound(node(pos));
	if(it!=s.end()&&it->l==pos)return it;
	--it;
	int l=it->l,r=it->r;
	int val=it->val;
	s.erase(it);
	s.insert(node(l,pos-1,val));
	return s.insert(node(pos,r,val)).first;
}
void assign(int l,int r,int val){
	si itr=spilt(r+1),itl=spilt(l);
	s.erase(itl,itr);
	s.insert(node(l,r,val)); 
}
void add(int l,int r,int val){
	si itr=spilt(r+1),itl=spilt(l);
	for(si it=itl;it!=itr;it++){
		it->val+=val;
	}
}
int kth(int l,int r,int k){//求第k小数
	si itr=spilt(r+1),itl=spilt(l);
	vector<pair<int,int>>v;
	for(si it=itl;it!=itr;it++){
		v.push_back(pair<int,int>(it->val,it->r-it->l+1));
	}
	sort(v.begin(),v.end());
	for(int i=0;i<v.size();i++){
		k-=v[i].second;
		if(k<=0)return v[i].first;
	}
	return -1;
}
int qsm(int a,int b,int p){
	int ans=1;
	a=a%p;
	for(;b;b>>=1){
		if(b&1)ans=(ans*a)%p;
		a=(a*a)%p;
	}
	return ans;
}
int query(int l,int r,int x,int y){
	si itr=spilt(r+1),itl=spilt(l);
	int res=0;
	for(si it=itl;it!=itr;it++){
		res=(res+(it->r-it->l+1)*qsm(it->val,x,y))%y;
	}
	return res;
}
int n, m, vmax;
int seed;
int rnd() {
    int ret = (int)seed;
    seed = (seed * 7 + 13) % 1000000007;
    return ret;
}
signed main(){
	FAST;
	cin>>n>>m>>seed>>vmax;
	for(int i=1;i<=n;i++){
		int a=rnd()%vmax+1;
		s.insert(node(i,i,a));
	}
	s.insert(node(n+1,n+1,0));
	for(int i=1;i<=m;i++){
		int l,r,x,y;
        int op = rnd() % 4 + 1;
        l = rnd() % n + 1, r = rnd() % n + 1;
        if (l > r) swap(l, r);
        if (op == 3) x = rnd() % (r - l + 1) + 1;
        else x = rnd() % vmax + 1;
        if (op == 4) y = rnd() % vmax + 1;
        if (op == 1) add(l, r, x);
        else if (op == 2) assign(l, r, x);
        else if (op == 3) printf("%lld\n", kth(l, r, x));
        else if (op == 4) printf("%lld\n", query(l, r, x, y));
	}
	return 0;
}

(5)dsu on tree

在这里插入图片描述

#include<bits/stdc++.h>
#define FAST ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define int long long
using namespace std;
const int N=1e5+7;
vector<int>q[N];
int sizes[N],son[N],d[N],f[N],Son=0,sum=0,a[N];
int cnt[N];
int MAXN=0;
int ans[N];
inline int read() {
    char c = getchar(); int x = 0, f = 1;
    while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}
void add(int x,int fa,int v){
	cnt[a[x]]+=v;
	if(cnt[a[x]]>MAXN)MAXN=cnt[a[x]],sum=a[x];
	else if(cnt[a[x]]==MAXN)sum+=a[x];
	for(int i=0;i<q[x].size();i++){
		if(q[x][i]!=fa&&q[x][i]!=Son){
			add(q[x][i],x,v);
		}
	}
}
void dfs1(int x,int fa){
	sizes[x]=1,son[x]=0;
	for(int i=0;i<q[x].size();i++){
		if(q[x][i]==fa)continue;
		dfs1(q[x][i],x);
		sizes[x]+=sizes[q[x][i]];
		if(sizes[son[x]]<sizes[q[x][i]])son[x]=q[x][i]; 
	}
}
void dfs2(int x,int fa,bool flag){
	for(int i=0;i<q[x].size();i++){
		if(q[x][i]!=fa&&q[x][i]!=son[x])
		dfs2(q[x][i],x,0);
	}
	if(son[x])dfs2(son[x],x,1),Son=son[x];
	add(x,fa,1);
	Son=0;
	ans[x]=sum;
	if(!flag)add(x,fa,-1),sum=0,MAXN=0;
}
signed main(){
	int n;
	n=read();
	for(int i=1;i<=n;i++){
		a[i]=read();
	} 
	int x,y;
	for(int i=1;i<n;i++){
		x=read(),y=read();
		q[x].push_back(y);
		q[y].push_back(x);
	}
	dfs1(1,0);
	dfs2(1,0,0);
	for(int i=1;i<=n;i++){
		printf("%lld ",ans[i]);
	}
	return 0;
}

(6)LCA

1.倍增法且最近公共祖先

#include<bits/stdc++.h>
using namespace std;
struct zzz {
    int t, nex;
}e[500010 << 1]; 
int head[500010], tot;
void add(int x, int y) {
	e[++tot].t = y;
	e[tot].nex = head[x]; 
	head[x] = tot;
}
int depth[500001], fa[500001][22], lg[500001];
void dfs(int now, int fath) {
	fa[now][0] = fath; depth[now] = depth[fath] + 1;
	for(int i = 1; i <= (int)(log(depth[now])/log(2))+1; ++i)
		fa[now][i] = fa[fa[now][i-1]][i-1];
	for(int i = head[now]; i; i = e[i].nex)
		if(e[i].t != fath) dfs(e[i].t, now);
}
int LCA(int x, int y) {
	if(depth[x] < depth[y]) swap(x, y);
	while(depth[x] > depth[y])
		x = fa[x][(int)(log(depth[x]-depth[y])/log(2))];
	if(x == y) return x;
	for(int k = (int)(log(depth[x])/log(2)); k >= 0; --k)
		if(fa[x][k] != fa[y][k])
			x = fa[x][k], y = fa[y][k];
	return fa[x][0];
}
int main() {
	int n, m;
	scanf("%d", &n);
	int xx;
	for(int i = 1; i <= n; ++i) {
		int x, y; scanf("%d%d", &x, &y);
		if(y==-1){
			xx=x;
		}
		add(x, y); add(y, x);
	}
	dfs(xx, 0);
	scanf("%d",&m);
	for(int i = 1; i <= m; ++i) {
		int x, y, z; scanf("%d%d",&x, &y);
		if(LCA(x,y)==x){
			cout<<1<<endl;
		}
		else if(LCA(x,y)==y){
			cout<<2<<endl;
		}
		else {
			cout<<0<<endl;
		}
	} 
	return 0;
}

2.树链剖分求最近公共祖先

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+7;
int sise[N],d[N],son[N],f[N],top[N];
vector<int>a[N];
void add(int x,int y){
	a[x].push_back(y);
}
void dfs1(int x,int fa){
	sise[x]=1;d[x]=d[fa]+1;
	son[x]=0;f[x]=fa;
	int len=a[x].size();
	for(int i=0;i<len;i++){
		if(f[x]==a[x][i])continue;
		else{
			dfs1(a[x][i],x);
			sise[x]+=sise[a[x][i]];
			if(sise[son[x]]<sise[a[x][i]])
			son[x]=a[x][i];
		}
	}
}
void dfs2(int x,int topx){
	top[x]=topx;
	if(son[x]!=0){ 
		dfs2(son[x],topx);
	}
	int len=a[x].size();
	for(int i=0;i<len;i++){
		if(a[x][i]!=f[x]&&a[x][i]!=son[x])
		dfs2(a[x][i],a[x][i]);
	}
}
int LCA(int x,int y){
	while(top[x]!=top[y]){
		if(d[top[x]]<d[top[y]]){
			swap(x,y);
		}
		x=f[top[x]];
	}
	return d[x]<d[y]?x:y;
} 
int main(){
	int n;
	cin>>n;
	int root=-1;
	for(int i=1;i<=n;i++){
		int x,y;
		cin>>x>>y;
		if(y==-1)root=x;
		else{
			add(x,y);
			add(y,x);
		}
	}
	dfs1(root,0);
	dfs2(root,root);
	int m;
	cin>>m;
	for(int i=1;i<=m;i++){
		int x,y;
		cin>>x>>y;
		if(LCA(x,y)==x){
			cout<<1<<"\n";
		}
		else if(LCA(x,y)==y){
			cout<<2<<"\n";
		}
		else cout<<0<<"\n";
	}
	return 0;
}

3.求k级祖先

int jump(int x,int k){
	if(d[x]-k<=0)return -1;
	while(k>=nid[x]-nid[top[x]]+1){
		k-=(nid[x]-nid[top[x]]+1);
		x=f[top[x]];
	}
	return oid[nid[x]-k];
}

(7)树上差分

树上差分总的来说就是对一颗树进行差分。树上差分可以分为点差分和边查分

点查分:

diff[u]+=x,diff[v]+=x,diff[o]-=x,diff[f[o]]-=x;o=LCA(u,v);

在这里插入图片描述

边差分:

diff[u]+=x,diff[v]+=x,diff[o]-=2*x;o=LCA(u,v);

在这里插入图片描述

(8)树链剖分

树链剖分基础

#include<bits/stdc++.h>
#define FAST ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define int long long
using namespace std;
const int N=1e6+7;
int d[N],f[N],size[N],son[N],top[N],nid[N],oid[N],dfn,a[N];
vector<int>q[N];
int n,m,r,p;
struct node{
	int l;
	int r;
	int v;
	int lazy;
}tree[N*4];
void pushup(int x){
	tree[x].v=(tree[x<<1].v+tree[x<<1|1].v)%p;
}
void pushdown(int x){
	if(tree[x].lazy!=0){
		tree[x<<1].v=(tree[x<<1].v%p+(tree[x<<1].r-tree[x<<1].l+1)%p*tree[x].lazy%p)%p;
		tree[x<<1|1].v=(tree[x<<1|1].v%p+(tree[x<<1|1].r-tree[x<<1|1].l+1)%p*tree[x].lazy%p)%p;
		tree[x<<1].lazy=(tree[x<<1].lazy+tree[x].lazy)%p;
		tree[x<<1|1].lazy=(tree[x<<1|1].lazy+tree[x].lazy)%p;
		tree[x].lazy=0;
	}
}
void built(int l,int r,int x){
	if(l==r){
		tree[x]={l,r,a[oid[l]]%p,0};
		return;
	}
	tree[x]={l,r,0,0};
	int mid=(l+r)/2;
	built(l,mid,x<<1);
	built(mid+1,r,x<<1|1);
	pushup(x); 
}
void modify(int l,int r,int v,int x){
	if(tree[x].l>=l&&tree[x].r<=r){
		tree[x].v=(tree[x].v%p+(tree[x].r-tree[x].l+1)%p*v%p)%p;
		tree[x].lazy=(tree[x].lazy+v)%p;
		return;
	}
	int mid=(tree[x].l+tree[x].r)/2;
	pushdown(x);
	if(l<=mid)modify(l,r,v,x<<1);
	if(r>mid)modify(l,r,v,x<<1|1);
	pushup(x);
}
int query(int l,int r,int x){
	if(tree[x].l>=l&&tree[x].r<=r){
		return tree[x].v%p;
	}
	int v=0;
	pushdown(x);
	int mid=(tree[x].l+tree[x].r)/2;
	if(l<=mid)v=(v+query(l,r,x<<1))%p;
	if(r>mid)v=(v+query(l,r,x<<1|1))%p;
	return v%p;
}
void dfs1(int x,int fa){
	d[x]=d[fa]+1;f[x]=fa;
	size[x]=1;son[x]=0;
	for(int i=0;i<q[x].size();i++){
		if(q[x][i]==fa)continue;
		dfs1(q[x][i],x);
		size[x]+=size[q[x][i]];
		if(size[son[x]]<size[q[x][i]])son[x]=q[x][i];
	}
}
void dfs2(int x,int topx){
	top[x]=topx;
	nid[x]=++dfn;
	oid[dfn]=x;
	if(son[x]!=0)dfs2(son[x],topx);
	for(int i=0;i<q[x].size();i++){
		if(q[x][i]!=f[x]&&son[x]!=q[x][i]){
			dfs2(q[x][i],q[x][i]);
		}
	}
}
void chain(int x,int y,int v){
	while(top[x]!=top[y]){
		if(d[top[x]]<d[top[y]])swap(x,y);
		modify(nid[top[x]],nid[x],v,1);
		x=f[top[x]];
	}
	if(d[x]>d[y])swap(x,y);
	modify(nid[x],nid[y],v,1);
}
int chain(int x,int y){
	int v=0;
	while(top[x]!=top[y]){
		if(d[top[x]]<d[top[y]])swap(x,y);
		v=(v+query(nid[top[x]],nid[x],1))%p;
		x=f[top[x]];
	}
	if(d[x]>d[y])swap(x,y);
	v=(v+query(nid[x],nid[y],1))%p;
	return v%p;
}
signed main(){
	FAST;
	cin>>n>>m>>r>>p;
	for(int i=1;i<=n;i++)cin>>a[i];
	int x,y,z;
	for(int i=1;i<n;i++){
		cin>>x>>y;
		q[x].push_back(y);
		q[y].push_back(x);
	}
	dfs1(r,0);
	dfs2(r,r);
	built(1,n,1);
	int op;
	for(int i=1;i<=m;i++){
		cin>>op;
		if(op==1){
			cin>>x>>y>>z;
			chain(x,y,z);
		}
		else if(op==2){
			cin>>x>>y;
			cout<<chain(x,y)%p<<"\n";
		}
		else if(op==3){
			cin>>x>>z; 
			modify(nid[x],nid[x]+size[x]-1,z,1);
		}
		else{
			cin>>x;
			cout<<query(nid[x],nid[x]+size[x]-1,1)%p<<"\n";
		}
	}
	return 0;
}

树链剖分与线段树综合应用

在这里插入图片描述

思路:根据深度来判断是否是加还是减。

#include<bits/stdc++.h>
#define FAST ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define int long long
using namespace std;
const int N=1e6+7;

vector<int>q[N];
int a[N],sizes[N],f[N],d[N],son[N],top[N],nid[N],oid[N],idex;
struct node{
	int l;
	int r;
	int v;
	int lazy;
}tree[N*4];
void dfs1(int x,int fa){
	sizes[x]=1,f[x]=fa;
	d[x]=d[fa]+1,son[x]=0;
	for(int i=0;i<q[x].size();i++){
		if(q[x][i]!=fa){
			dfs1(q[x][i],x);
			sizes[x]+=sizes[q[x][i]];
			if(sizes[son[x]]<sizes[q[x][i]]){
				son[x]=q[x][i];
			} 
		}
	}
}
void dfs2(int x,int topx){
	top[x]=topx;
	nid[x]=++idex;
	oid[idex]=x;
	if(son[x])dfs2(son[x],topx);
	for(int i=0;i<q[x].size();i++){
		if(q[x][i]!=f[x]&&q[x][i]!=son[x]){
			dfs2(q[x][i],q[x][i]);
		}
	}
}
void pushdown(int x){
	if(tree[x].lazy!=0){
		if((tree[x<<1].r-tree[x<<1].l+1)==1){
			if(d[oid[tree[x<<1].l]]%2==1){
				tree[x<<1].v+=tree[x].lazy; 
			} 
			else tree[x<<1].v-=tree[x].lazy;
		}
		else{
			tree[x<<1].lazy+=tree[x].lazy;
		}
		if((tree[x<<1|1].r-tree[x<<1|1].l+1)==1){
			if(d[oid[tree[x<<1|1].l]]%2==1){
				tree[x<<1|1].v+=tree[x].lazy; 
			} 
			else tree[x<<1|1].v-=tree[x].lazy;
		}
		else{
			tree[x<<1|1].lazy+=tree[x].lazy;
		}
		tree[x].lazy=0;
	}
}
void built(int l,int r,int x){
	if(l==r){
		tree[x]={l,r,a[oid[l]],0};
		return;
	}
	tree[x]={l,r,0,0};
	int mid=(l+r)/2;
	built(l,mid,x<<1);
	built(mid+1,r,x<<1|1);
}
void modify(int l,int r,int v,int x){
	if(tree[x].l>=l&&tree[x].r<=r){
		if(tree[x].l==tree[x].r){
			if(d[oid[tree[x].l]]%2){
				tree[x].v+=v;
			}
			else tree[x].v-=v;
		}
		else tree[x].lazy+=v;
		return;
	}
	pushdown(x);
	int mid=(tree[x].l+tree[x].r)/2;
	if(l<=mid)modify(l,r,v,x<<1);
	if(r>mid)modify(l,r,v,x<<1|1);
}
int query(int pos,int x){
	if(tree[x].l==tree[x].r){
		return tree[x].v;
	}
	pushdown(x);
	int mid=(tree[x].r+tree[x].l)/2;
	if(pos<=mid)return query(pos,x<<1);
	else return query(pos,x<<1|1);
}
signed main(){
	FAST;
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	int x,y;
	for(int i=1;i<n;i++){
		cin>>x>>y;
		q[x].push_back(y);
		q[y].push_back(x);
	}
	dfs1(1,0);
	dfs2(1,1);
	built(1,n,1);
	for(int i=1;i<=m;i++){
		int op,x,y;
		cin>>op;
		if(op==1){
			cin>>x>>y;
			if(d[x]%2)modify(nid[x],sizes[x]+nid[x]-1,y,1);
			else modify(nid[x],sizes[x]+nid[x]-1,-y,1);
		}    
		else{
			cin>>x;
			cout<<query(nid[x],1)<<"\n";
		}
	}
	return 0;
}

(9)主席树

在这里插入图片描述

#include<bits/stdc++.h>
#define FAST ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define int long long
using namespace std;
const int N=5e6+7;
int root[N],idex;
int a[N];
vector<int>ve;
struct node{
	int l;
	int r;
	int cnt;
}tree[N];
int find(int x){
	lower_bound(ve.begin(),ve.end(),x)-ve.begin(); 
}
int built(int l,int r){//建树
	int p=++idex;
	if(l==r){
		return p;
	}
	int mid=(l+r)/2;
	tree[p].l=built(l,mid);
	tree[p].r=built(mid+1,r);
	return p;
}
int insert(int l,int r,int x,int v){//插入
	int p=++idex;
	tree[p]=tree[x];
	if(l==r){
		tree[p].cnt++;
		return p;
	}
	int mid=(l+r)/2;
	if(v<=mid)tree[p].l=insert(l,mid,tree[x].l,v);
	else tree[p].r=insert(mid+1,r,tree[x].r,v);
	tree[p].cnt=tree[tree[p].l].cnt+tree[tree[p].r].cnt;//pushup操作
	return p; 
} 
int query(int x1,int x2,int l,int r,int k){//线段树上二分
	if(l==r){
		return l;
	}
	int mid=(l+r)/2;
	int cnt=tree[tree[x1].l].cnt-tree[tree[x2].l].cnt;
	if(k<=cnt)return query(tree[x1].l,tree[x2].l,l,mid,k);
	else return query(tree[x1].r,tree[x2].r,mid+1,r,k-cnt);
}
signed main(){
	FAST;
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		ve.push_back(a[i]);
	}
	sort(ve.begin(),ve.end());
	ve.erase(unique(ve.begin(),ve.end()),ve.end());
	root[0]=built(0,ve.size()-1);
	for(int i=1;i<=n;i++){
		root[i]=insert(0,ve.size()-1,root[i-1],find(a[i]));.
	}
	int l,r,k;
	for(int i=1;i<=m;i++){	
		cin>>l>>r>>k;
		cout<<ve[query(root[r],root[l-1],0,ve.size()-1,k)]<<"\n";
	} 
	return 0;
}

(10)维护区间问题

维护区间问题通常是要求将区间排序,然后用双指针、set上二分、线段树来维护区间。

给一些线段,每个现段有 l , r , w 三个属性。现在要求选出一个集合,使得集合中的线段的并覆盖区间 [ 1 , m ] ,并且线段的权值之和最小。

#include<bits/stdc++.h>
#define FAST ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define int long long
using namespace std;
const int N=1e6+7;
struct node{
	int l,r,v;
	int minn;
	int lazy;
}tree[N*4];
void pushup(int x){
	tree[x].minn=min(tree[x<<1].minn,tree[x<<1|1].minn);
	tree[x].v=tree[x<<1].v+tree[x<<1|1].v;
}
void pushdown(int x){
	if(tree[x].lazy){
		tree[x<<1].lazy+=tree[x].lazy;
		tree[x<<1|1].lazy+=tree[x].lazy;
		tree[x<<1].v+=(tree[x<<1].r-tree[x<<1].l+1)*tree[x].lazy;
		tree[x<<1|1].v+=(tree[x<<1|1].r-tree[x<<1|1].l+1)*tree[x].lazy;
		tree[x<<1].minn+=tree[x].lazy;
		tree[x<<1|1].minn+=tree[x].lazy;
		tree[x].lazy=0;
	}
}
void built(int x,int l,int r){
	tree[x]={l,r,0,0,0};
	if(l==r){
		return;
	}
	int mid=(l+r)>>1;
	built(x<<1,l,mid);
	built(x<<1|1,mid+1,r);
}
void modify(int x,int l,int r,int v){
	if(tree[x].l>=l&&tree[x].r<=r){
		tree[x].v+=(tree[x].r-tree[x].l+1)*v;
		tree[x].minn+=v;
		tree[x].lazy+=v;
		return;
	}
	pushdown(x);
	int mid=(tree[x].l+tree[x].r)>>1;
	if(l<=mid)modify(x<<1,l,r,v);
	if(r>mid)modify(x<<1|1,l,r,v);
	pushup(x);
} 
struct nod{
	int l,r,v;
}a[N];
bool cmp(const nod xx,const nod yy){
	return xx.v<yy.v;
}
signed main(){
//	FAST;
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i].l>>a[i].r>>a[i].v;
	}
	built(1,1,m-1);
	sort(a+1,a+1+n,cmp);
	int b=1,e=0;
	int ans=INT_MAX;
	while(1){
		while(!tree[1].minn&&e!=n){
			e++;
			modify(1,a[e].l,a[e].r-1,1);
		}
		while(tree[1].minn){
			ans=min(a[e].v-a[b].v,ans);
			modify(1,a[b].l,a[b].r-1,-1);
			b++;
		}
		if(e==n)break; 
	}
	cout<<ans;
	return 0;
}

如何快速的判断一个区间集合是否都与某区间Q相交?

如果区间集合内存在一个区间A不与区间Q相交,那么一定满足区间A的右端点 小于 区间Q的左端点,或区间A的左端点 大于 区间Q的右端点,所以我们只需维护区间集合内 右端点最小的位置sp 和 左端点最大的位置ep,如果满足ep > Q.r 或sp < Q.l ,证明区间集合内存在某区间不与区间Q相交。

(11)字符串

1.kmp

KMP算法就是字符串匹配算法,具体代码如下。

kmp模板

#include<bits/stdc++.h>
#define FAST ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define int long long
using namespace std;
const int N=1e6+7;
char str1[N],str2[N];
int nex[N];
int n,m;
void getnext(char *a){
	int j=0;
	for(int i=2;i<=n;i++){
		while(j&&a[i]!=a[j+1])j=nex[j];
		if(a[i]==a[j+1]){
			j++;
		}
		nex[i]=j;
	}
}
int KMP(char *b,char *a){
	for(int i=1,j=0;i<=m;i++){
		while(j&&b[i]!=a[j+1])j=nex[j];
		if(b[i]==a[j+1])j++;
		if(j==n){
			cout<<i-n<<" ";
			j=nex[j]; 
		} 
	}
}
signed main(){
	FAST;
	cin>>n>>str1+1>>m>>str2+1;//str1为模式串(短的),str2为文本串(长的)
	getnext(str1);
	KMP(str2,str1);
	return 0;
}

kmp+dp

在这里插入图片描述

#include<bits/stdc++.h>
#define FAST ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define int long long
using namespace std;
const int N=1e6+7;
const int mod=1e9+7;
char a[N];
int nex[N];
int dp[101][100001];
int n,m;
void getnext(char *str){
	int j=0;
	for(int i=2;i<=m;i++){
		while(j&&str[i]!=str[j+1])j=nex[j];
		if(str[i]==str[j+1]){
			j++;
		}
		nex[i]=j;
	}
}
signed main(){
	FAST;
	cin>>n;
	cin>>(a+1);
	m=strlen(a+1);
	getnext(a);
	dp[0][0]=1; 
	for(int i=1;i<=n;i++){
		for(int j=0;j<m;j++){
			for(int k=0;k<26;k++){
				int u=j;
				while(u&&a[u+1]!=(char)(k+'a'))u=nex[u];
				if(a[u+1]==(char)(k+'a'))u++;
				if(u<m)dp[i][u]=(dp[i][u]+dp[i-1][j])%mod;
			}
		}
	}
	int ans=0;
	for(int i=0;i<m;i++){
		ans=(ans+dp[n][i])%mod;
	}
	cout<<ans;
	return 0;
}

2.AC自动机

在这里插入图片描述

在这里插入图片描述

#include<bits/stdc++.h>
#define FAST ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define int long long
using namespace std;
const int N=1e6+7;
char a[N];
int trie[N][26];
int cnt[N];
int idex;
int vis[N];
int q[N];
int fail[N];
void insert(char *str,int pos){
	int p=0;
	for(int i=0;str[i];i++){
		int u=str[i]-'a';
		if(!trie[p][u])trie[p][u]=++idex;
		p=trie[p][u];
		cnt[p]++;
	}
	vis[pos]=p;
}
void query(){
	int hh=0,tt=0;
	for(int i=0;i<26;i++){
		if(trie[0][i])q[tt++]=trie[0][i];
	}
	while(tt!=hh){
		int x=q[hh++];
		for(int i=0;i<26;i++){
			if(trie[x][i]){
				fail[trie[x][i]]=trie[fail[x]][i];
				q[tt++]=trie[x][i];
			}
			else trie[x][i]=trie[fail[x]][i];
		}
	}
	for(int i=idex;i>=1;i--){
		cnt[fail[q[i]]]+=cnt[q[i]];
	}
}
signed main(){
	FAST;
	int n;
	cin >>n;
	for(int i=1;i<=n;i++){
		cin>>a;
		insert(a,i);
	}
	query();
	for(int i=1;i<=n;i++){
		cout<<cnt[vis[i]]<<"\n";
	}
	return 0;
}

ACAM last优化

void init(){
    int tt=0,hh=0;
    for(int i=0;i<26;i++){
        if(trie[0][i]){
            q[tt++]=trie[0][i];
        }
    }
    while(tt!=hh){
        int x=q[hh++];
        for(int i=0;i<26;i++){
            if(trie[x][i]){
                fail[trie[x][i]]=trie[fail[x]][i];
                q[tt++]=trie[x][i];
                if(d[fail[trie[x][i]]]){
                    last[trie[x][i]]=fail[trie[x][i]];
                }
                else last[trie[x][i]]=last[fail[trie[x][i]]];
            } 
            else{
                trie[x][i]=trie[fail[x]][i];
            }
        }
    }
} 

在这里插入图片描述

#include<bits/stdc++.h>
#define FAST ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define int long long
using namespace std;
const int mod=1e4+7;
const int N=1e6+7;
int fail[N];
int idex;
int q[N];
char a[N];
bool vis[N];
int trie[N][26];
int dp[2][N];
void insert(char *str){
	int p=0;
	for(int i=0;str[i];i++){
		int u=str[i]-'A';
		if(!trie[p][u])trie[p][u]=++idex;
		p=trie[p][u];
	}
	vis[p]=1;
}
void solve(){
	int hh=0,tt=0;
	for(int i=0;i<26;i++){
		if(trie[0][i])q[tt++]=trie[0][i];
	}
	while(hh!=tt){
		int x=q[hh++];
		for(int i=0;i<26;i++){
			if(trie[x][i]){
				fail[trie[x][i]]=trie[fail[x]][i];
				q[tt++]=trie[x][i];
				vis[trie[x][i]]|=vis[fail[trie[x][i]]];
			}
			else{
				trie[x][i]=trie[fail[x]][i];
			}
		}
	}
}
int qsm(int a,int b){
	int ans=1;
	for(;b;b>>=1){
		if(b&1)ans=(a*ans)%mod;
		a=(a*a)%mod; 
	}
	return ans;
}
signed main(){
	FAST;
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a;
		insert(a);
	}
	solve();
	dp[0][0]=1;
	for(int i=1;i<=m;i++){
		for(int j=0;j<=idex;j++){
			for(int k=0;k<26;k++){
				if(!vis[trie[j][k]]){
					dp[i&1][trie[j][k]]=(dp[i&1][trie[j][k]]+dp[(i-1)&1][j])%mod;
				}
			}
		}
		for(int j=0;j<=idex;j++)dp[(i-1)&1][j]=0;
	}
	int ans=0;
	for(int i=0;i<=idex;i++){
		(ans+=dp[m&1][i])%=mod;
	}
	int xx=qsm(26,m);
	cout<<(xx-ans+mod)%mod;
	return 0;
}

3.回文自动机

#include<bits/stdc++.h>
#define FAST ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define int long long
using namespace std;
const int N=3e6+7;
char a[N];
int len[N];
int trie[N][26];
int fail[N];
int cnt[N];
int now,idex=1;
int getfail(int p,int i){
	while(i-len[p]-1<=0||a[i-len[p]-1]!=a[i])
	p=fail[p];
	return p;
}
int insert(int u,int pos){
	int p=getfail(now,pos);
	if(!trie[p][u]){
		fail[++idex]=trie[getfail(fail[p],pos)][u];
		trie[p][u]=idex;
		len[idex]=len[p]+2;
		cnt[idex]=cnt[fail[idex]]+1; 
	}
	now=trie[p][u];
	return cnt[now];
}
signed main(){
	FAST;
	cin>>(a+1);
	int lena=strlen(a+1);
	fail[0]=1,len[1]=-1;
	int last=0;
	for(int i=1;i<=lena;i++){
		if(i>1){
			a[i]=(a[i]-'a'+last)%26+'a';
			last=insert(a[i]-'a',i);
		}
		else last=insert(a[i]-'a',i);
		cout<<last<<" ";
	}
	return 0;
}

4.Manacher算法

#include <bits/stdc++.h>
#define FAST ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
using namespace std;
const int N = 2e7 + 7;
char a[N];
char b[N<<1];
int p[N<<1];//i表示最长回文中心,p[i]为最长回文半径。
bool mapp[N];
signed main(){
    FAST;
    int t;
    t=1;
    while (t--){
        cin>>a;
        int len=strlen(a);
        int cnt=0;
        b[++cnt]='^';
        for(int i = 0;i<len;i++){
            b[++cnt]='#';
            b[++cnt]=a[i];
        }
        b[++cnt]='#',b[++cnt]='&';
        int mr=0, mid=0;
        int maxn=0;
        for(int i = 0; i < cnt; i++{
            if (i <= mr)p[i]=min(p[mid*2-i],mr-i+1);
            while(b[i-p[i]]==b[i+p[i]])p[i]++;
            if (i+p[i]-1>= mr){
                mr=i+p[i]-1, mid = i;
            }
            maxn=max(maxn, p[i]);
        }
        cout<<maxn-1;
    }
    return 0;
}

(12)字符串哈希

1.一般字符串哈希

#include<bits/stdc++.h>
#define FAST ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define int long long
#define ull unsigned long long
using namespace std;
const int N=1e6+7;
char a[N];
char b[N];
ull pre[N];
ull has[N];
ull ha[N];
void init(int len){
	pre[0]=1;
	for(int i=1;i<=len;i++){	
		pre[i]=pre[i-1]*1331;
		has[i]=has[i-1]*1331+a[i]; 
	}
} 
int check(int l,int r){
	return has[r]-has[l-1]*pre[r-l+1];
}
int sum[N];
int que[N];
int ans[N];
void modify(int l,int r,int x){
	sum[l]+=x;
	sum[r+1]-=x;
}
signed main(){
	FAST;
	cin>>(a+1);
	cin>>(b+1);
	int lena=strlen(a+1);
	init(lena);
	int lenb=strlen(b+1); 
	for(int i=1;i<=lenb;i++){
		ha[i]=ha[i-1]*1331+b[i];
	}
	for(int i=1;i+lenb-1<=lena;i++){
		if(check(i,i+lenb-1)==ha[lenb])cout<<i<<" ";
	}
	return 0;
}

2.二维字符串哈希

#include<bits/stdc++.h>
#define FAST ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define int long long
using namespace std;
const int N=1010;
unsigned int has[N][N];
char a[N][N];
char b[N][N];
unsigned int pre1[N];
unsigned int pre2[N];
unsigned int hh[N][N];
int s[N*N];		
int n,m;
void init(){
	pre1[0]=pre2[0]=1;
	for(int i=1;i<=n;i++){
		pre1[i]=pre1[i-1]*131;
	}
	for(int i=1;i<=m;i++){
		pre2[i]=pre2[i-1]*1331;
	}	
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			has[i][j]=has[i-1][j]*131+has[i][j-1]*1331+a[i][j]-has[i-1][j-1]*131*1331;	
		}
	}
}
int check(int x1,int y1,int x2,int y2){
	return has[x2][y2]-(has[x1-1][y2]*pre1[x2-x1+1])-(has[x2][y1-1]*pre2[y2-y1+1])+(has[x1-1][y1-1]*pre1[x2-x1+1]*pre2[y2-y1+1]);
}
signed main(){
	FAST;
	int t;
	cin>>t;
	while(t--){
		cin>>n>>m;
		int cnt=0;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				cin>>a[i][j];
			} 
		}
		init();
		int x,y;
		cin>>x>>y;
		for(int i=1;i<=x;i++){
			for(int j=1;j<=y;j++){
				cin>>b[i][j]; 
				hh[i][j]=hh[i-1][j]*131+hh[i][j-1]*1331+b[i][j]-hh[i-1][j-1]*131*1331;
			}
		}
		int ans=0;
		for(int i=1;i+x-1<=n;i++){
			for(int j=1;j+y-1<=m;j++){
				int x2=i+x-1,y2=j+y-1;
				if(hh[x][y]==check(i,j,x2,y2)){
					ans++;
				}
			}
		}
		cout<<ans<<"\n";
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				has[i][j]=0;
			}
		}
		for(int i=1;i<=x;i++){
			for(int j=1;j<=y;j++){
				hh[i][j]=0;
			}
		}
	}
	return 0;
}

(13)高精度模板

1.加法

高精加高精

// C = A + B, A >= 0, B >= 0
vector<int> add(vector<int> &A, vector<int> &B)//逆序存,返回逆序 
{
    if (A.size() < B.size()) return add(B, A);
    vector<int> C;
    int t = 0;
    for (int i = 0; i < A.size(); i ++ )
    {
        t += A[i];
        if (i < B.size()) t += B[i];
        C.push_back(t % 10);
        t /= 10;
    }
    if (t) C.push_back(t);
    return C;
}

2.减法

高精减高精

// C = A - B, 满足A >= B, A >= 0, B >= 0
vector<int> sub(vector<int> &A, vector<int> &B)//逆序存,返回逆序 
{
    vector<int> C;
    for (int i = 0, t = 0; i < A.size(); i ++ )
    {
        t = A[i] - t;
        if (i < B.size()) t -= B[i];
        C.push_back((t + 10) % 10);
        if (t < 0) t = 1;
        else t = 0;
    }
    while (C.size() > 1 && C.back() == 0) C.pop_back();
    return C;
}

3.乘法

高精乘低精

vector<int> mul(vector<int> &A, int b)//高精逆序存,低精顺序存,返回逆序 
{
    vector<int> C;
    int t = 0;
    for (int i = 0; i < A.size() || t; i ++ )
    {
        if (i < A.size()) t += A[i] * b;
        C.push_back(t % 10);
        t /= 10;
    }
    while (C.size() > 1 && C.back() == 0) C.pop_back();
    return C;
}

高精乘高精

vector<int> mul(vector<int> &A, vector<int> &B)//逆序存,返回逆序 
{
    vector<int> C;
    for(int i=1;i<=A.size()*B.size();i++)C.push_back(0); 
    for(int i=0;i<A.size();i++){
    	int x=0;
    	for(int j=0;j<B.size();j++){
    		x=x+A[i]*B[j]+C[i+j];
    		C[i+j]=x%10;
			x=x/10;
		}
		C[i+B.size()]=C[i+B.size()]+x;
	}
    while (C.size() > 1 && C.back() == 0) C.pop_back();
    return C;
}

4.除法

高精除低精

vector<int> div(vector<int> &A, int b, int &r)//逆序存,低精顺序存,返回逆序(b为除数,r为余数) 
{
    vector<int> C;
    r = 0;
    for (int i = A.size() - 1; i >= 0; i -- )
    {
        r = r * 10 + A[i];
        C.push_back(r / b);
        r %= b;
    }
    reverse(C.begin(), C.end());
    while (C.size() > 1 && C.back() == 0) C.pop_back();
    return C;
}

字符串方法

#include <bits/stdc++.h>
using i64 = long long;
std::string operator^(std::string a, std::string b) {
    std::string c;
    reverse(a.begin(), a.end());
    reverse(b.begin(), b.end());
    int p = 0;
    for (int i = 0; i < int(a.size()) || i < int(b.size()); i++) {
        if (i < int(a.size())) p += a[i] - '0';
        if (i < int(b.size())) p += b[i] - '0';
        c += p % 10 + '0', p /= 10;
    }
    if (p) c += p + '0';
    reverse(c.begin(), c.end());
    return c;
}
bool relat(std::string &a, std::string &b) {
    if (a.size() != b.size()) return a.size() > b.size();
    for (int i = 0; i < int(a.size()); i++) {
        if (a[i] != b[i]) return a[i] > b[i];
    }
    return true;
}
std::string operator-(std::string a, std::string b) {
    std::string c;
    bool ok = true;
    if (!relat(a, b)) std::swap(a, b), ok = false, c += '-';
    reverse(a.begin(), a.end());
    reverse(b.begin(), b.end());
    int p = 0;
    for (int i = 0; i < int(a.size()); i++) {
        p = a[i] - '0' - p;
        if (i < int(b.size())) p -= b[i] - '0';
        c += ((p + 10) % 10) + '0';
        if (p < 0) p = 1;
        else p = 0;
    }
    while (c.size() > 1 && c.back() == '0') c.pop_back();
    if (ok) reverse(c.begin(), c.end());
    else reverse(c.begin() + 1, c.end());
    return c;
}
std::string operator*(std::string a, i64 b) {
    std::string c;
    reverse(a.begin(), a.end());
    i64 p = 0;
    for (int i = 0; i < int(a.size()) || p; i++) {
        if (i < int(a.size())) p += (a[i] - '0') * b;
        c += p % 10 + '0', p /= 10;
    }
    while (c.size() > 1 && c.back() == '0') c.pop_back();
    reverse(c.begin(), c.end());
    return c;
}
std::string operator/(std::string a, i64 b) {
    std::string c;
    i64 r = 0;
    for (int i = 0; i < int(a.size()); i++) {
        r = r * 10 + a[i] - '0';
        c += r / b + '0', r %= b;
    }
    reverse(c.begin(), c.end());
    while (c.size() > 1 && c.back() == '0') c.pop_back();
    reverse(c.begin(), c.end());
    return c;
}
std::string operator%(std::string a, i64 b) {
    std::string c;
    i64 r = 0;
    for (int i = 0; i < int(a.size()); i++) {
        r = r * 10 + a[i] - '0';
        c += r / b + '0', r %= b;
    }
    reverse(c.begin(), c.end());
    while (c.size() > 1 && c.back() == '0') c.pop_back();
    reverse(c.begin(), c.end());
    return std::to_string(r);
}
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    int n;
    std::cin >> n;
    std::string ans = "1";
    for (int i = 1; i <= n; i++) {
        ans = ans * i;
        ans = ans * i;
    }
    ans = ans * i64(1E9);
    for (int i = 0; i < n; i++) {
        ans = ans / n;
    }
    std::cout << ans / i64(1E9) << "." << ans % i64(1E9) << "\n";
    return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值