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 N≤100,M≤10
输入样例
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 1≤n≤200
输入样例:
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,表示点ai和bi之间存在一条权值为ci的边。
输出格式
输出一个整数,表示树的最长路径的长度。
数据范围
1 ≤ n ≤ 10000 1≤n≤10000 1≤n≤10000
1 ≤ a i , b i ≤ n 1≤ a_{i},b_{i}≤n 1≤ai,bi≤n
− 1 0 5 ≤ c i ≤ 1 0 5 -10^5≤ci≤10^5 −105≤ci≤105
输入样例:
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 1≤m≤16,1≤n,pi≤109
输入样例:
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=Ca−1b−1+Cab−1
#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<p且b<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=0∑ncnkxkan−k
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,b−a,c−b,....)
Φ ( 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∗(1−p11)∗(1−p21)∗...∗(1−pn1) ( p k 为 n 的质因数 ) (p_{k}为n的质因数) (pk为n的质因数)
2.卡特兰数
卡特兰数前n项
pos | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|---|
v | 1 | 1 | 2 | 5 | 14 | 132 | 429 | 1430 | 4862 | 16796 | 58786 |
卡特兰数递归定义
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(n−1)+F(1)∗F(n−2)+......+F(n−1)∗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+14n−2F(n−1)
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+11C2nn⇔C2nn−C2nn−1
卡特兰数的例题
答案是 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=51[(21+5)n−(21−5)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}
[FnFn−1]=[1110]n−1∗[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();
}
}
}
线段树合并的综合应用
思路:本题的主要采用的算法是树上差分+线段树合并
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探