一.分治
二.前缀和,差分,离散化
1.前缀和
求区间和常用
s[i]=s[i-1]+a[i]
s[i][j]=a[i][j]+s[i-1][j]+s[i][j-1]-s[i-1][j-1]
查询
s[r]---s[l-1]
二维查询一个矩形:
例题1:一维前缀和
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,a[100005],sum[100005];
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
sum[i]=sum[i-1]+a[i];
}
cin>>m;
for(int i=0;i<m;i++){
int x,y;
cin>>x>>y;
cout<<sum[y]-sum[x-1]<<endl;
}
}
例题2:二维前缀和
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,a[125][125],sum=-1e9;
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>a[i][j];
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1];
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int x1=1;x1<=n;x1++){
for(int x2=1;x2<=n;x2++){
if(x1<i||x2<j)continue;
else {
sum=max(sum,a[x1][x2]-a[x1][j-1]-a[i-1][x2]+a[i-1][j-1]);
}
}
}
}
}
cout<<sum<<'\n';
return 0;
}
2.差分
d[i]=a[i]-a[i-1]
给某个区间加一个值,用差分数组操作
a: 1 2 3 4 5
d: 1 1 1 1 1
[1,3]+1 2 1 1 0 1
[2,4]+1 2 2 1 0 0
[3,5]+1 2 2 2 0 -1
求前缀和 2 4 6 6 6--->a'
第一项加一,后一项减一
d[l]+=d;
d[r+1]-=d;
d[i]不变
做题一般是求出差分数组,修改差分数组,对差分数组进行求前缀和
a->d->d'->a'
二维差分:
a[i][j]=s[i][j]-s[i-1][j]-s[i][j-1]+s[i-1][j-1]
例题1:一维差分
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,p,l,r,fen,min1,a[5000005],s[5000005],s1[500005];
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>p;
for(int i=1;i<=n;i++){
cin>>s[i];
}
for(int i=1;i<=n;i++){
a[i]=s[i]-s[i-1];
}
for(int i=0;i<p;i++){
cin>>l>>r>>fen;
if(l>r){
int t=0;
t=l;
l=r;
r=t;
}
if(l==0)l++;
a[l]+=fen;
a[r+1]-=fen;
}
for(int i=1;i<=n;i++){
s1[i]=s1[i-1]+a[i];
}
min1=s1[1];
for(int i=1;i<=n;i++){
min1=min(s1[i],min1);
}
cout<<min1<<'\n';
return 0;
}
例题2:二维差分
#include <iostream>
#include <vector>
#include <string>
#include <cmath>
#include <algorithm>
#include <iomanip>
#include <map>
#include <vector>
#include <queue>
#include <set>
//#include<bits/stdc++.h>
using namespace std;
#define int long long
int n, m, x1, z, x2, y2, s[1005][1005], a[1005][1005];
signed main()
{
cin >> n >> m;
for (int i = 1;i <= m;i++) {
cin >> x1 >> z >> x2 >> y2;
a[x1][y2 + 1]--;
a[x1][z]++;
a[x2 + 1][y2 + 1]++;
a[x2 + 1][z]--;
}
for (int i = 1;i <= n;i++) {
for (int j = 1;j <= n;j++) {
s[i][j] = a[i][j] + s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
}
}
for (int i = 1;i <= n;i++) {
for (int j = 1;j <= n;j++) {
cout << s[i][j] << " ";
}
cout << endl;
}
return 0;
}
3.离散化
步骤:
- 传入ls[top++]
- 进行排序,小下标对应小数字
- unique去重,只能一个下标对应一个值----->它会把重复的元素添加到容器末尾,而返回值是去重之后的尾地址(是地址!!)
int num[10]={1,1,2,2,2,3,4,5,5,5}; int ans=unique(num,num+10)-num; //去重函数返回地址为:去重后最后一个不重复元素地址
- 查询,将原数组x[i]的值换成ls数组值对应的下标
for(int l=0;l<n;l++){ x[l].i=lower_bound(ls,ls+end,x[l].i)-ls; x[l].j=lower_bound(ls,ls+end,x[l].j)-ls; }
例题1:P1955
#include<bits/stdc++.h>
#define int long long
using namespace std;
int t,fa[1000005],ls[3000005];
bool p;
struct Mystruct
{
int i1,j1,e;
}aa[1000001];
bool cmd(Mystruct a,Mystruct b){
return a.e>b.e;
}
void csh(int d){
for(int i=1;i<=d;i++){
fa[i]=i;
}
}
int find(int d){
if(fa[d]==d) return d;
return fa[d]=find(fa[d]);
}
void link(int d,int e){
int l=find(d);
int r=find(e);
fa[l]=r;
}
signed main(){
cin>>t;
int top=0;
for(int i=0;i<t;i++){
int n;
p=0;
top=0;
cin>>n;
memset(fa, 0, sizeof(fa));
memset(ls, 0, sizeof(ls));
memset(aa, 0, sizeof(aa));
for(int j=1;j<=n;j++){
cin>>aa[j].i1>>aa[j].j1>>aa[j].e;
ls[top++]=aa[j].i1;
ls[top++]=aa[j].j1;
}
sort(ls,ls+top);
int znd=unique(ls,ls+top)-ls;
for(int j=1;j<=n;j++){
aa[j].i1=lower_bound(ls,ls+znd,aa[j].i1)-ls;
aa[j].j1=lower_bound(ls,ls+znd,aa[j].j1)-ls;
}
csh(znd);
sort(aa+1,aa+1+n,cmd);
for(int j=1;j<=n;j++){
if(aa[j].e){
link(aa[j].i1,aa[j].j1);
//fa[find(aa[j].i1)]=find(aa[j].j1);
}
else if(find(aa[j].i1)==find(aa[j].j1)){
printf("NO\n");
p=1;
break;
}
}
if(p==0){
printf("YES\n");
}
}
return 0;
}
三.深度优先搜索dfs
一条路走到黑
例题1:P1036 选数
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,k,a[25],sum,ans;
bool ss(int x){
if(x<=1)return 0;
for(int i=2;i*i<x;i++){
if(x%i==0)return 0;
}
return 1;
}
void dfs(int sum,int y,int x){//有y用于标记到哪里了,不重复取前面的数
if(x==k){
if(ss(sum)){
ans++;
return;
}
}
for(int i=y;i<n;i++){
sum+=a[i];
dfs(sum,i+1,x+1);
sum-=a[i];
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>k;
for(int i=0;i<n;i++){
cin>>a[i];
}
dfs(0,0,0);
cout<<ans<<'\n';
return 0;
}
例题2:B3621 枚举元组
不需要标记到哪了,可以重复取前面的数
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,k,a[10];
vector<int>res;
void dfs(int x){
if(x==n){
for(int i=0;i<n;i++){
cout<<res[i]<<" ";
}
cout<<'\n';
return;
}
for(int i=1;i<=k;i++){
res.push_back(a[i]);
dfs(x+1);
res.pop_back();
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>k;
for(int i=1;i<=k;i++){
a[i]=i;
}
dfs(0);
return 0;
}
例题3: P1596 Lake Counting S
找水坑 从有开始找
#include <iostream>
#include <vector>
#include <string>
#include <cmath>
#include <algorithm>
#include <iomanip>
#include <map>
#include <vector>
#include <queue>
#include <set>
//#include<bits/stdc++.h>
using namespace std;
#define int long long
int n, m, ans;
bool res;
//vector<vector<char>> a;
//vector<vector<bool>> b;
char a[105][105];
bool b[105][105];
int z[8][2] = { {-1,-1}, {-1,0}, {-1,1}, {0,-1}, {0,1}, {1,-1}, {1,0}, {1,1} };
void dfs(int sx, int sy) {
b[sx][sy] = 1;
for (int i = 0;i < 8;i++) {
int tx = sx + z[i][0];
int ty = sy + z[i][1];
if (tx >= 0 && tx < n && ty >= 0 && ty < m && a[tx][ty] == 'W' && b[tx][ty] == 0) {
//b[tx][ty] = 1;
dfs(tx, ty);
}
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
//a.resize(n,vector<char>(m));
//b.resize(n,vector<bool>(m,0));
for (int i = 0;i < n;i++) {
for (int j = 0;j < m;j++) {
cin >> a[i][j];
}
}
for (int i = 0;i < n;i++) {
for (int j = 0;j < m;j++) {
if (a[i][j] == 'W' && b[i][j] == 0) {
dfs(i, j);//进入一个水坑,进行寻找周围是否有连着的W
ans++;//出来则ans++说明结束一个水坑
}
}
}
cout << ans;
return 0;
}
例题4:P10483 小猫爬山
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,w,sum[20],a[20],ans,cnt;//sum[i]每个车的容量,a[i]小猫重量
bool b;
bool cmp(int a, int b) {
return a > b;
}
void dfs(int che,int cat){
if(cat==n+1){//达到猫总量return
b=1;
cout<<i<<'\n';
return;
}
if(b==1) return ;
for(int i=1;i<=che;i++){
if(sum[i]-a[cat]>=0){
sum[i]-=a[cat];
dfs(che,cat+1);
sum[i]+=a[cat];
}
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>w;
for(int i=1;i<=n;i++){
cin>>a[i];
ans+=a[i];
}
for(int i=1;i<=n;i++){
sum[i]=w;//每个车都是w容量
}
sort(a+1,a+n+1,cmp);//排序
if(ans%w==0)cnt=ans/w;
else cnt=ans/w+1;//求到车最的小需求量cnt
for(int i=cnt;i<=n;i++){
b=0;
dfs(i,1);//遍历,从cnt开始到最大需求量n,猫是会在dfs中++的
if(b){
cout<<i<<'\n';
break;
}
}
return 0;
}
例题5:B3625 迷宫寻路
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n, m;
bool b[105][105], res;
vector<vector<char>> a;
int z[4][2] = {{1,0}, {0,1}, {-1,0}, {0,-1}};
void dfs(int sx, int sy) {
if (sx == n-1 && sy == m-1) {
res = 1;
}
for (int i = 0; i < 4; i++) {
int tx = sx + z[i][0];
int ty = sy + z[i][1];
if (tx >= 0 && tx < n && ty >= 0 && ty < m && !b[tx][ty] && a[tx][ty] != '#') {
b[tx][ty] = 1;
dfs(tx, ty);
//b[tx][ty] = 0;走过是墙 不需要再走一遍 不用标记回去
}
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
a.resize(n, vector<char>(m));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> a[i][j];
}
}
b[0][0] = 1;
if(a[0][0]=='#'){
cout << "No" << '\n';
return 0;
}
dfs(0, 0);
if (res) cout << "Yes" << '\n';
else
cout << "No" << '\n';
return 0;
}
四.广度优先搜索bfs
找最短时间/路径
一般会用到
queue<pair<int,int>>q;
while(!q.empty()){
int x1=q.front().first;
int y1=q.front().second;
q.pop();
for(int i=0;i<8;i++){
int tx=x1+z[i][0];
int ty=y1+z[i][1];
if(tx>0&&tx<=n&&ty>0&&ty<=m&&b[tx][ty]==0)
{
q.push(make_pair(tx,ty));
ans[tx][ty]=ans[x1][y1]+1;
b[tx][ty]=1;
}
}
}
例题1:P1443马的遍历
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,x,y;
int z[8][2]={{-2,1},{-2,-1},{-1,2},{-1,-2},{1,2},{1,-2},{2,1},{2,-1}};//往能走的方向走
bool b[405][405];//用于标记走过的地方
int ans[405][405];
queue<pair<int,int>>q;//用于执行dfs
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m>>x>>y;
q.push(make_pair(x,y));
while(!q.empty()){
int x1=q.front().first;
int y1=q.front().second;
q.pop();
for(int i=0;i<8;i++){
int tx=x1+z[i][0];
int ty=y1+z[i][1];
if(tx>0&&tx<=n&&ty>0&&ty<=m&&b[tx][ty]==0)
{
q.push(make_pair(tx,ty));
ans[tx][ty]=ans[x1][y1]+1;
b[tx][ty]=1;
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(i==x&&j==y){
cout<<0<<setw(5);
}
else if(ans[i][j]==0){
cout<<-1<<setw(5);
}
else cout<<ans[i][j]<<setw(5);
}
cout<<'\n';
}
return 0;
}
例题2:P1162填涂颜色
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
int z[4][2]={{1,0},{0,1},{0,-1},{-1,0}};
bool b[35][35];
int a[35][35];
void bfs(int x,int y){
queue<pair<int,int>>q;
q.push(make_pair(x,y));
while(!q.empty()){
int x=q.front().first;
int y=q.front().second;
q.pop();
//b[x][y]=1;
for(int i=0;i<4;i++){
int tx=x+z[i][0];
int ty=y+z[i][1];
if(tx>=0&&tx<n&&ty>=0&&ty<n&&a[tx][ty]==0&&b[tx][ty]==0){
q.push(make_pair(tx,ty));
b[tx][ty]=1;
}
}
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
cin>>a[i][j];
}
}
//外圈往里面走
for (int i = 0; i < n; ++i) {
if (a[i][0] == 0 && b[i][0]==0) {
bfs(i, 0);
}
if (a[i][n-1] == 0 && b[i][n-1]==0) {
bfs(i, n-1);
}
}
for (int j = 0; j < n; ++j) {
if (a[0][j] == 0 && b[0][j]==0) {
bfs(0, j);
}
if (a[n-1][j] == 0 && b[n-1][j]==0) {
bfs(n-1, j);
}
}
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
if (a[i][j] == 0 && b[i][j]==0) {
cout << "2 ";
} else {
cout << a[i][j] << " ";
}
}
cout << endl;
}
return 0;
}
五.STL
六.并查集
模板
int find(int x) //查找结点 x的根结点
{
if(pre[x] == x) return x; //递归出口:x的上级为 x本身,即 x为根结点
return pre[x] = find(pre[x]); //此代码相当于先找到根结点 rootx,然后pre[x]=rootx
}
void join(int x,int y)
{
int fx=find(x), fy=find(y);
if(fx != fy)
pre[fx]=fy;
}
或者路径压缩(加权标记)
void union(int x,int y)
{
x=find(x); //寻找 x的代表元
y=find(y); //寻找 y的代表元
if(x==y) return ; //如果 x和 y的代表元一致,说明他们共属同一集合,则不需要合并,直接返回;否则,执行下面的逻辑
if(rank[x]>rank[y]) pre[y]=x; //如果 x的高度大于 y,则令 y的上级为 x
else //否则
{
if(rank[x]==rank[y]) rank[y]++; //如果 x的高度和 y的高度相同,则令 y的高度加1
pre[x]=y; //让 x的上级为 y
}
}
例题1:P3367 【模板】并查集
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,z,x,y,pre[10005],r[10005];
int find(int x){
return pre[x]==x?x:pre[x]=find(pre[x]);
}
void unions(int x,int y){
x=find(x);
y=find(y);
if(r[x]>r[y]){
pre[y]=x;
}
else{
pre[x]=y;
if(r[x]==r[y]){
r[x]++;
}
}
}
signed main(){
cin>>n>>m;
for(int i=1;i<10005;i++){
pre[i]=i;
//rank[i]=1;
}
for(int i=0;i<m;i++){
cin>>z>>x>>y;
if(z==1){
unions(x,y);
}
if(z==2){
if(find(x)==find(y)){
cout<<"Y"<<endl;
}
else cout<<"N"<<endl;
}
}
return 0;
}
例题2:P1551 亲戚
#include <iostream>
#include <vector>
#include <string>
#include <cmath>
#include <algorithm>
#include <iomanip>
#include <deque>
#define int long long
const int N = 1e5 + 9;
using namespace std;
int n, m, p,pre[N],r[N];
int find(int x)
{
return pre[x]= pre[x] == x ? x : find(pre[x]);
}
void unions(int x, int y)
{
x = find(x);y = find(y);
if (x==y)return;
else
{
if (r[x] > r[y])swap(x, y);
pre[x] = y;
if (r[x] == r[y])r[y]++;
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n>> m>>p;
int x, y, a, b;
//vector<int>x(n), y(n),a(n),b(n);
for (int i = 0;i < n;i++)
{
pre[i] = i;
}
for (int i = 0;i < m;i++)
{
cin >> x>> y;
unions(x, y);
}
for (int i = 0;i < p;i++)
{
cin >> a>> b;
if (find(a) == find(b))
{
cout << "Yes" << '\n';
}
else cout << "No" << '\n';
}
return 0;
}
七.二分
二分函数
- binary_search 查找某个元素是否出现
若在数组(要求数组元素非递减)中查找到indx元素则真,若查找不到则返回值为假
binary_search(arr,arr+size,indx)
arr: 数组首地址
size:数组元素个数
indx: 需要查找的值
- lower_bound 查找第一个大于或等于某个元素的位置。
前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置(注意是地址)。如果所有元素都小于val,则返回last的下一位置
lower_bound(arr[],arr[]+size , indx):
arr[]: 数组首地址
size:数组元素个数
indx: 需要查找的值
#include<bits/stdc++.h>
using namespace std;
int main(){
int a[6]={1,2,3,2,2,2};
sort(a,a+6);//[1,2,2,2,2,3]
int cnt=lower_bound(a,a+6,3)-a;//得到索引位
cout<<cnt;//5
return 0;
}
- upper_bound 查找第一个大于某个元素的位置。
upper_bound(arr[],arr[]+size , indx):
arr[]: 数组首地址
size:数组元素个数
indx:需要查找的值
#include<bits/stdc++.h>
using namespace std;
int main(){
int a[6]={1,2,3,2,2,2};
sort(a,a+6);//[1,2,2,2,2,3]
int cont=upper_bound(a,a+6,2)-a;
cout<<cont;//5
return 0;
}
例题1: P2249 查找
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,a[1000006],x;
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=0;i<n;i++){
cin>>a[i];
}
for(int i=0;i<m;i++){
cin>>x;
if(binary_search(a,a+n,x)){
cout<<lower_bound(a,a+n,x)-a+1<<" ";
}
else cout<<-1<<" ";
}
return 0;
}
例题2: P1102 A-B 数对
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,c,ans,a[200005];
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>c;
for(int i=0;i<n;i++){
cin>>a[i];
}
sort(a,a+n);
for(int i=0;i<n;i++){
if(a[i]>c){
int x1=upper_bound(a,a+n,a[i]-c)-a;
int x2=lower_bound(a,a+n,a[i]-c)-a;
if(x2!=n){
ans+=abs(x1-x2);
}
}
}
cout<<ans;
return 0;
}
八.最短路径算法、最小生成树算法
最短路径
P4779 【模板】单源最短路径(标准版)
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,s,dist[100005];
struct node{
int y,v;
node(int _y,int _v){
y=_y;
v=_v;
}
};
vector<node>edge[100005];
set<pair<int,int>>se;
void dij(int s){
memset(dist,127,sizeof(dist));
dist[s]=0;
se.clear();
for(int i=0;i<=n;i++){
se.insert(make_pair(dist[i],i));
}
while(!se.empty()){
int x=se.begin()->second;
se.erase(se.begin());
for(auto i:edge[x]){
if(dist[x]+i.v<dist[i.y]){
se.erase(make_pair(dist[i.y],i.y));
dist[i.y]=dist[x]+i.v;
se.insert(make_pair(dist[i.y],i.y));
}
}
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m>>s;
for(int i=0;i<m;i++){
int u,y,v;
cin>>u>>y>>v;
edge[u].push_back(node(y,v));
}
dij(s);
for(int i=1;i<=n;i++){
cout<<dist[i]<<" ";
}
return 0;
}
最短路经过的点:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,s,t,dist[100005],a[10005];
int parent[100005];//*
struct node{
int y,v;
node(int _y,int _v){
y=_y;
v=_v;
}
};
vector<node>edge[100005];
set<pair<int,int>>se;
void dij(int s){
for(int i=0;i<100005;i++){
dist[i]=1e15;
}
dist[s]=0;
se.clear();
for(int i=0;i<=n;i++){
se.insert(make_pair(dist[i],i));
}
while(!se.empty()){
int x=se.begin()->second;
se.erase(se.begin());
for(auto i:edge[x]){
if(dist[x]+i.v<dist[i.y]){
se.erase(make_pair(dist[i.y],i.y));
dist[i.y]=dist[x]+i.v;
parent[i.y] = x;//*
se.insert(make_pair(dist[i.y],i.y));
}
}
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m>>s>>t;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=0;i<m;i++){
int u,y,v;
cin>>u>>y>>v;
edge[u].push_back(node(y,v));
edge[y].push_back(node(u,v));
}
dij(s);
if(dist[t]>=1e9){
cout<<"Impossible" <<'\n';
}
else {
// 输出最短路径经过的点
vector<int> path;
int current = t;
while (current != s) {
path.push_back(current);
current = parent[current];
}
path.push_back(s);
reverse(path.begin(), path.end());
for (int i = 0; i < path.size(); i++) {
cout<<path[i]<<" ";
}
return 0;
}
例题:RC-u4 City 不 City
#include<bits/stdc++.h>
#define int long long
using namespace std;
int hlt,n,m,s,t,dist[100005],a[10005];
int parent[100005];
struct node{
int y,v,rd;
node(int _y,int _v,int _rd){
y=_y;
v=_v;
rd=_rd;
}
};
vector<node>edge[100005];
set<pair<int,int>>se;
void dij(int s,int t){
for(int i=0;i<100005;i++){
dist[i]=1e15;
}
dist[s]=0;
se.clear();
for(int i=0;i<=n;i++){
se.insert(make_pair(dist[i],i));
}
while(!se.empty()){
int x=se.begin()->second;
se.erase(se.begin());
for(auto i:edge[x]){
if(dist[x]+i.v<dist[i.y]){
se.erase(make_pair(dist[i.y],i.y));
dist[i.y]=dist[x]+i.v;
parent[i.y] = max(parent[x],i.rd);
se.insert(make_pair(dist[i.y],i.y));
if(i.y==t) hlt=x;
}
else if(dist[x]+i.v==dist[i.y]&&parent[x]<i.rd){
se.erase(make_pair(dist[i.y],i.y));
dist[i.y]=dist[x]+i.v;
parent[i.y] = max(parent[x],i.rd);
se.insert(make_pair(dist[i.y],i.y));
if(i.y==t) hlt=x;
}
}
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m>>s>>t;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=0;i<m;i++){
int u,y,v;
cin>>u>>y>>v;
edge[u].push_back(node(y,v,a[y]));
edge[y].push_back(node(u,v,a[u]));
}
dij(s,t);
if(dist[t]>=1e9){
cout<<"Impossible" <<'\n';
}
else {
cout<<dist[t]<<" "<<parent[hlt];
}
return 0;
}
最小生成树
例题1:P3366 【模板】最小生成树
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,dist[200005];
struct node{
int y,v;
node(int _y,int _v){
y=_y;v=_v;
}
};
vector<node>edge[200005];
set<pair<int,int>>se;
bool pd[100005];
int p()
{
int n0=0,ans=0;
for(int i=0;i<=200005;i++){
dist[i]=1e30;
}
se.clear();
dist[1]=0;
for(int i=0;i<=n;i++){
se.insert(make_pair(dist[i],i));
}
while(!se.empty()){
int x=se.begin()->second;
se.erase(se.begin());
if(dist[x]>1e9)break;//1e9!!!!!!!!!!
n0++;
ans+=dist[x];
pd[x]=1;
for(auto i:edge[x]){
if(pd[i.y]==0&&i.v<dist[i.y]){
se.erase(make_pair(dist[i.y],i.y));
dist[i.y]=i.v;
se.insert(make_pair(dist[i.y],i.y));
}
}
}
if(n0!=n){
return -1;
}
else return ans;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=0;i<m;i++){
int x,y,z;
cin>>x>>y>>z;
edge[x].push_back(node(y,z));
edge[y].push_back(node(x,z));
}
int t=p();
if(t==-1)cout<<"orz";
else cout<<t;
return 0;
}
最小瓶颈路
最小瓶颈路:两个结点之间的最长边最短的一条路径
例题1:
#include <bits/stdc++.h>
#define int long long
using namespace std;
struct Edge {
int u, v, a;
bool operator<(const Edge &other) const {
return a < other.a;
}
};
vector<int> pre, rank1;
int find(int x) {
if (pre[x] != x) {
pre[x] = find(pre[x]);
}
return pre[x];
}
void unite(int x, int y) {
int rootX = find(x);
int rootY = find(y);
if (rootX != rootY) {
if (rank1[rootX] > rank1[rootY]) {
pre[rootY] = rootX;
} else if (rank1[rootX] < rank1[rootY]) {
pre[rootX] = rootY;
} else {
pre[rootY] = rootX;
rank1[rootX]++;
}
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
int n, m;
cin >> n >> m;
vector<Edge> edges(m);
for (int i = 0; i < m; ++i) {
cin >> edges[i].u >> edges[i].v >> edges[i].a;
--edges[i].u;
--edges[i].v;
}
int s, t;
cin >> s >> t;
--s;
--t;
pre.resize(n);
rank1.assign(n, 0);
iota(pre.begin(), pre.end(), 0);
sort(edges.begin(), edges.end());
for (const auto &edge : edges) {
unite(edge.u, edge.v);
if (find(s) == find(t)) {
cout << edge.a << '\n';
return 0;
}
}
cout << -1 << '\n';
return 0;
}
九.动态规划
1.背包
01背包
思路:
①考虑前i个物品,记录总体积为0,1,.......,m时的最大收益
②考虑了前i个物品,总体积为j时的情况有两种:
1.第 i 个物品没取,考虑第前 i-1个物品,总体积为 j 时的情况
2.第 i 个物品取了,考虑第前 i-1个物品,总体积为 j-v[i] 时的情况
优化:
考虑了前 i 个物品时的状态只和考虑了前i-1个物品的状态有关,前面i-2行都不需要记啦
将g数组赋值给f数组
memcpy(f,g,sizeof(g));
最终优化:
完全背包
思路:
①依旧是记录最大收益
② 考虑了前i个物品,总体积为j时的情况有两种:
1.第 i 个物品没取,考虑第前 i-1个物品,总体积为 j 时的情况
2.第 i 个物品取了,考虑第前 i 个物品,总体积为 j-v[i] 时的情况
优化:
多重背包
小于100
小于1000:
分组背包
思路:
优化:
2.简单dp
十.构造法
十一.模拟法
十二.拓扑排序
例题1:有向环图判断
例题2:字典序最小/最大的拓扑序列
默认大根堆 !!!
十三.二分图的最大匹配(匈牙利算法)
二分图判定
二分图匹配
例题1:棋盘覆盖问题
思路:
例题2:最大独立集
最大独立集合=n-最大匹配数
例题3:最小点覆盖
最小点覆盖 = 最大匹配数
例题4:最小路径覆盖
十四.最大流的增广路算法
十五.哈夫曼树
十六.树
十七.数学
1.组合数学
1.1加法原理
1.2乘法原理
1.3排列组合
1.4递推关系
2.关于模运算
①加法
int result = (a % mod + b % mod) % mod;
②减法 处理非负
int result = (a % mod - b % mod + mod) % mod;
③乘法
int result = (a % mod * b % mod) % mod;
④除法 要求逆元
int b_inverse = mod_inverse(b, mod); // 计算 b 的逆元
int result = (a % mod * b_inverse % mod) % mod;
3.逆元
①扩展欧几里得算法求逆元
使用条件:基本上通用,不要求p为质数,且效率高,时间复杂度为。
void extend_gcd(ll a, ll b, ll &x, ll &y){
if(b == 0){
x = 1, y = 0;
return;
}
extend_gcd(b, a % b, x, y);
ll tmp = x;
x = y;
y = tmp - (a / b) * y;
}
ll mod_inverse(ll a, ll mod){
ll x, y;
extend_gcd(a, mod, x, y);
return (x % mod + mod) % mod;
}
②费马小定理
使用条件:要求p为质数,效率也挺高,但由于扩展欧几里得算法通常情况下更优,因此该方法使用情况较少,时间复杂度为。
ll qmi(ll a, ll b, ll mod){
ll res = 1;
while(b){
if(b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
ll fermat(ll a, ll mod){
return qmi(a, mod - 2, mod);
}
费马小定理+快速幂求逆元
③欧拉定理 拓展欧拉定理
使用条件:不要求p为质数,这相当于费马小定理求逆元的扩展,先求出欧拉函数,再求逆元,时间复杂度为。 a、n互质
int get_phi(int m){//欧拉函数 (1--n-1的互质个数)
int res=m;
for(int i=2;i*i<=m;i++){
if(m%i==0){
res=res/i*(i-1);
while(m%i==0)m/=i;
}
}
if(m>1)res=res/m*(m-1);
return res;
}
int depow(string s,int phi){//降幂 b以字符串形式输入 底下再转stoll
int b=0;
bool flag = false;
for(int i=0;s[i];i++){
b=b*10+(s[i]-'0');
if(b>=phi){
flag=1;
b%=phi;
}
}
if(flag)b+=phi;
return b;
}
int qmi(int a, int n, int m) { // 快速幂算法-->加上取模防止a^b很大溢出
int res = 1;
a %= m; // 确保底数在模数范围内
while (n) {
if (n & 1) res = (res * a) % m;
a = (a * a) % m;
n >>= 1;
}
return res;
}
简化幂模运算
phi--> 依次取模降幂
例题:P5091 【模板】扩展欧拉定理
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 1e9 + 7;
int a,m;
string s; //先用字符串存b因为b很大
int get_phi(int m){//欧拉函数 (1--n-1的互质个数)
int res=m;
for(int i=2;i*i<=m;i++){
if(m%i==0){
res=res/i*(i-1);
while(m%i==0)m/=i;
}
}
if(m>1)res=res/m*(m-1);
return res;
}
int depow(string s,int phi){//降幂
int b=0;
bool flag = false;
for(int i=0;s[i];i++){
b=b*10+(s[i]-'0');
if(b>=phi){
flag=1;
b%=phi;
}
}
if(flag)b+=phi;
return b;
}
int qmi(int a, int n, int m) { // 快速幂算法-->加上取模防止a^b很大溢出
int res = 1;
a %= m; // 确保底数在模数范围内
while (n) {
if (n & 1) res = (res * a) % m;
a = (a * a) % m;
n >>= 1;
}
return res;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>a>>m>>s;
int phi=get_phi(m);
int b=0;
if (s == "0") { // 任何数的 0 次方都是 1
cout << 1 << endl;
return 0;
}
if (s.size() > 18 || stoll(s) >= phi) { // 如果 s 转换成整数大于 phi
b = depow(s, phi);
} else {
b = stoll(s); // 小于 phi,直接转换
}
int res = qmi(a, b, m); // 计算 a^b mod m
cout<<res<<endl;
return 0;
}
例题: 阶乘 逆元
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1e5+5;
const ll mod=998244353;
ll inv[maxn], fac[maxn]; //分别表示逆元和阶乘
//快速幂
ll quickPow(ll a,ll b){
ll ans=1;
while(b){
if(b&1)
ans=(ans*a)%mod;
b>>=1;
a=(a*a)%mod;
}
return ans;
}
void init(){
//求阶乘
fac[0]=1;
for(int i=1;i<maxn;i++){
fac[i]=fac[i-1]*i%mod;
}
//求逆元
inv[maxn-1]=quickPow(fac[maxn-1],mod-2);
for(int i=maxn-2;i>=0;i--){
inv[i]=inv[i+1]*(i+1)%mod;
}
}
ll C(int n,int m){
if(m>n){
return 0;
}
if(m==0)
return 1;
return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int main(){
init();
int n,m;
scanf("%d%d",&n,&m);
int sum=0;
for(int i=0;i<n;i++) {
int a;
cin>>a;
sum+=a-1;
}
m-=sum;
printf("%lld\n",(C(m-1,n)+C(m-1,n-1))%mod);
}
4.中国剩余定理
解决一类同余方程组的问题,适合处理带有不同模数的情况
5.矩阵快速幂
① 快速幂
例题:p3390【模板】矩阵快速幂
#include <bits/stdc++.h>
#define int long long
using namespace std;
#define lc (p<<1)
#define rc (p<<1|1)
int n,k;
const int mod=1000000007;
struct matrix{
int c[101][101];//矩阵大小
matrix(){memset(c,0,sizeof c);}
}A,res;//A存原始矩阵,res存计算结果
matrix operator*(matrix &x,matrix &y){
matrix t;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int k=1;k<=n;k++){
t.c[i][j]=(t.c[i][j]+x.c[i][k]*y.c[k][j])%mod;
}
}
}
return t;
}
void quickpow(int k){
for(int i=1;i<=n;i++)res.c[i][i]=1;//单位矩阵最开始 因为单位矩阵乘等于该矩阵本身
while(k){
if(k&1)res=res*A;
A=A*A;
k>>=1;
}
}
signed main() {
cin>>n>>k;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>A.c[i][j];
}
}
quickpow(k);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cout<<res.c[i][j]<<" ";
}
cout<<endl;
}
return 0;
}
② 矩阵加速递推
例题:p1962斐波那契数列
#include <bits/stdc++.h>
#define int long long
using namespace std;
#define lc (p<<1)
#define rc (p<<1|1)
int n;
const int mod=1000000007;
struct matrix{
int c[3][3];
matrix(){memset(c,0,sizeof c);}
}A,F;//A存原始矩阵,F为斐波那契矩阵
matrix operator*(matrix &x,matrix &y){
matrix t;
for(int i=1;i<=2;i++){
for(int j=1;j<=2;j++){
for(int k=1;k<=2;k++){
t.c[i][j]=(t.c[i][j]+x.c[i][k]*y.c[k][j])%mod;
}
}
}
return t;
}
void quickpow(int n){
F.c[1][1]=F.c[1][2]=1;
A.c[1][1]=A.c[1][2]=A.c[2][1]=1;
while(n){
if(n&1)F=F*A;
A=A*A;
n>>=1;
}
}
signed main() {
cin>>n;
if(n==1){
cout<<1;
return 0;
}
quickpow(n-2);
cout<<F.c[1][1];
return 0;
}
6.数论
① 素数与整除问题
② 进制位
③ 最大公因数 最小公倍数
最大公因数
int gcd(int a, int b) {
while (b != 0) {
int tmp = a;
a = b;
b = tmp % b;
}
return a;
}
最小公倍数
④ 欧几里得算法拓展
⑤ 分解质因数 唯一分解定理 试除法
任何一个整数都可以分解成若干个质因数相乘
P2043 质因子分解
#include <bits/stdc++.h>
#define int long long
using namespace std;
#define lc (p<<1)
#define rc (p<<1|1)
int n,a[10005];
void dec(){
for (int i=2;i<=n;i++)
{
int xx=i;
for (int j=2;j<=i;j++)
while (xx%j==0) {
a[j]++; xx/=j;
}
}
}
signed main() {
cin>>n;
dec();
for(int i=1;i<=n;i++){
if(a[i])cout<<i<<" "<<a[i]<<endl;
}
return 0;
}
注:数论技巧
① 奇偶性
奇数+奇数=偶数
偶数+偶数=偶数
奇数+偶数=奇数
奇数×奇数=奇数
偶数×偶数=偶数
奇数×偶数=偶数
②数字根
指的是一个数字各位数字之和,直到结果为一位数
可以通过mod 9得到:
987-->9+8+7=24-->2+4=6----->987mod 9=6
十八.线段树--二十八
十九.kmp
二十.欧拉图
例题1:单词接龙
例题2:欧拉路判断
例题3:字典序最小的欧拉路
二十一.滑动窗口
例题1:
例题2:
例题3:
二十二.树状数组
单点更新 区间查询
上一个位置p相加
下一个位置
代码
例题1:比赛
链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
#include <bits/stdc++.h>
#define int long long
using namespace std;
int T,n,a[20005],tree[100005];
int llow[20010],lup[20010],rlow[20010],rup[20010],lcnt[20010],rcnt[20010];
int lowbit(int x){
return x&(-x);//二进制位最后一个1的位置的值
}
void update(int x, int k){
while(x<=100000) {
tree[x]+= k;
x+= lowbit(x);//更新tree[]个数
}
}
int query(int p){//求和 个数
if(p==0){
return 0;
}
return query(p-lowbit(p))+tree[p];
}
signed main()
{
cin>>T;
while(T--){
int ans=0;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
llow[i]=query(a[i]);//左边小于a[i]的数
lup[i]=query(100000)-query(a[i]-1);//左边大于a[i]的数
lcnt[i] = query(a[i])-query(a[i] - 1);//与a[i]相等的数
update(a[i],1);
}
for(int i=1;i<=100000;i++){
tree[i]=0;
}
for(int i=n;i>=1;i--){
rlow[i]=query(a[i]);
rup[i]=query(100000)-query(a[i]-1);
rcnt[i] = query(a[i])-query(a[i]-1);
update(a[i],1);
ans+=rlow[i]*lup[i];
ans+=llow[i]*rup[i];
ans-=lcnt[i]*rcnt[i];
}
for(int i=1;i<=100000;i++){
tree[i]=0;
}
cout<<ans<<'\n';
}
return 0;
}
二十三.倍增-最近公共祖先
倍增法求Lca(最近公共祖先)_parents[i][up] = parents[parents[i][up - 1]][up - -CSDN博客
二十四.欧拉筛、埃氏筛法
“最小质因数 × 最大因数(非自己) = 这个合数”
例题1:P3383 【模板】线性筛素数
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n,q;
bool a[100000010];
vector<int>b;
void ols(int n){
a[1]=1;
for(int i=2;i<=n;i++){
if(!a[i])b.push_back(i);
for(int j:b){
if(j*i>n)break;
a[j*i]=1;
if(i%j==0)break;
}
}
}
signed main() {
ols(100000000);
cin>>n>>q;
while(q--){
int x;cin>>x;
cout<<b[x-1]<<endl;
}
return 0;
}
二十五.st表
查询区间最大/最小值
例题:区间查询
#include <iostream>
#include <cmath>
#include <cstdio>
int num[1000005];
int st[25][1000005];//长度为i起始位置为j的最小值
#define max(a,b) (a>b?a:b)
using namespace std;
void init(int n)
{
for(int i=1;i<=n;++i)
scanf("%d",&num[i]);
for(int i=1;i<=n;++i)
st[0][i]=num[i];
for(int i=1;(1<<i)<=n;++i)
{
for(int j=1;j<=n;++j)//j+(1<<i)是某一段开始,-1表示上一段的结束
if(j+(1<<i)-1<=n)
st[i][j]=max(st[i-1][j],st[i-1][j+(1<<(i-1))]);
}
}
int query(int l,int r)
{
int k=int(log(r-l+1)/log(2.0));
return max(st[k][l],st[k][r-(1<<k)+1]);
}
int main()
{
int n,m;
scanf("%d",&n);
init(n);
scanf("%d",&m);
int a,b;
for(int i=0;i<m;++i)
{
scanf("%d%d",&a,&b);
printf("%d\n",query(a,b));
}
return 0;
}
二十六.字符串
1.字符串哈希 o(1)
例题1:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int P = 131;
const int MOD = 1e9 + 7;
// 计算字符串的哈希值
vector<int> ch(const string &s) {
int n = s.size();
vector<int> hashes(n + 1, 0);
vector<int> pows(n + 1, 1);
for (int i = 1; i <= n; ++i) {
//pows[i] = pows[i - 1] * P % MOD;
hashes[i] = (hashes[i - 1] * P + (s[i - 1] - 'a' + 1)) % MOD;
}
return hashes;
}
// 计算子串的哈希值
int gh(int l, int r, const vector<int> &hashes, const vector<int> &pows) {
return (hashes[r + 1] - hashes[l] * pows[r - l + 1] % MOD + MOD) % MOD;//防止出现负数
}
signed main() {
string s, t;
cin >> s >> t;
int lens = s.size();
int lent = t.size();
if (lent != lens + 1) {
cout << 0 << endl;
return 0;
}
vector<int> hs = ch(s);
vector<int> ht = ch(t);
vector<int> pows(lent, 1);
for (int i = 1; i <= lent; ++i) { //乘了几个P
pows[i] = pows[i - 1] * P % MOD;
}
vector<int> v1;
vector<char> v2;
for (int i = 0; i <= lens; ++i) {
int lt = gh(0, i - 1, ht, pows);
int rt = gh(i + 1, lent - 1, ht, pows);
int ls = gh(0, i - 1, hs, pows);
int rs = gh(i, lens - 1, hs, pows);
if (ls == lt && rs == rt) {
v1.push_back(i);
v2.push_back(t[i]);
}
}
cout << v1.size() << endl;
for (int i = 0; i < v1.size(); ++i) {
cout << v1[i] << " " << v2[i] << endl;
}
return 0;
}
二十七.图论-三元环 四元环
--------------------------------------------------------------------------------------------------------------------------------
二十八.线段树
区间修改和区间查询
例题:P3372 【模板】线段树 1
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, m, a[100005], p;
#define lc (p<<1)
#define rc (p<<1|1)
struct node {
int l, r, sum, add;
} tr[400005];
void pushup(int p) {
tr[p].sum = tr[lc].sum + tr[rc].sum;
}
// 建树
void build(int p, int l, int r) {
tr[p] = {l, r, a[l], 0};
if (l == r) return;
int m = (l + r) >> 1;
build(lc, l, m);
build(rc, m + 1, r);
pushup(p);
}
// 区间修改
void pushdown(int p) {
if(tr[p].l != tr[p].r) {//如果不是叶子节点,需要对其左右的子节点进行递归更新或查询
tr[lc].sum += tr[p].add * (tr[lc].r - tr[lc].l + 1);
tr[rc].sum += tr[p].add * (tr[rc].r - tr[rc].l + 1);
tr[lc].add += tr[p].add;
tr[rc].add += tr[p].add;
tr[p].add = 0;
}
}
void update(int p, int x, int y, int k) {
if (x <= tr[p].l && tr[p].r <= y) {
tr[p].sum += (tr[p].r - tr[p].l + 1) * k;
tr[p].add += k;
return;
}
int m = (tr[p].l + tr[p].r) >> 1;
pushdown(p);
if (x <= m) update(lc, x, y, k);
if (y > m) update(rc, x, y, k);
pushup(p);
}
int query(int p, int x, int y) {
if (x <= tr[p].l && tr[p].r <= y) {
return tr[p].sum;
}
int m = (tr[p].l + tr[p].r) >> 1;
pushdown(p);
int sum = 0;
if (x <= m) sum += query(lc, x, y);
if (y > m) sum += query(rc, x, y);
return sum;
}
signed main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
build(1, 1, n);
while (m--) {
int op; cin >> op;
if (op == 1) {
int l, r, k;
cin >> l >> r >> k;
update(1, l, r, k);
}
if (op == 2) {
int l, r;
cin >> l >> r;
int sum = query(1, l, r);
cout << sum << endl;
}
}
return 0;
}
例题:P3373 【模板】线段树 2
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, mod, a[100005], p,q;
#define lc (p<<1)
#define rc (p<<1|1)
const int MOD = 571373;
struct node {
int l, r, sum, mul, add;
} tr[400005];
void pushup(int p) {
tr[p].sum = (tr[lc].sum + tr[rc].sum)%mod;
}
// 建树
void build(int p, int l, int r) {
tr[p] = {l, r, a[l],1,0};
if (l == r) return;
int m = (l + r) >> 1;
build(lc, l, m);
build(rc, m + 1, r);
pushup(p);
}
// 区间修改
void pushdown(int p) {
if(tr[p].l != tr[p].r) {
tr[lc].sum = (tr[lc].sum * tr[p].mul + tr[p].add * (tr[lc].r - tr[lc].l + 1)) % mod;
tr[rc].sum = (tr[rc].sum * tr[p].mul + tr[p].add * (tr[rc].r - tr[rc].l + 1)) % mod;
tr[lc].mul = (tr[lc].mul * tr[p].mul) % mod;
tr[rc].mul = (tr[rc].mul * tr[p].mul) % mod;
tr[lc].add = (tr[lc].add * tr[p].mul + tr[p].add) % mod;
tr[rc].add = (tr[rc].add * tr[p].mul + tr[p].add) % mod;
tr[p].mul = 1;
tr[p].add = 0;
}
}
void update(int p, int x, int y, int k) {
if (x <= tr[p].l && tr[p].r <= y) {
tr[p].sum += ((tr[p].r - tr[p].l + 1) * k)%mod;
tr[p].add = (tr[p].add + k) % mod;
return;
}
int m = (tr[p].l + tr[p].r) >> 1;
pushdown(p);
if (x <= m) update(lc, x, y, k);
if (y > m) update(rc, x, y, k);
pushup(p);
}
void update1(int p, int x, int y, int k) {
if (x <= tr[p].l && tr[p].r <= y) {
tr[p].sum = (tr[p].sum * k) % mod;
tr[p].mul = (tr[p].mul * k) % mod;
tr[p].add = (tr[p].add * k) % mod;
return;
}
int m = (tr[p].l + tr[p].r) >> 1;
pushdown(p);
if (x <= m) update1(lc, x, y, k);
if (y > m) update1(rc, x, y, k);
pushup(p);
}
int query(int p, int x, int y) {
if (x <= tr[p].l && tr[p].r <= y) {
return tr[p].sum;
}
int m = (tr[p].l + tr[p].r) >> 1;
pushdown(p);
int sum = 0;
if (x <= m) sum= (sum+query(lc, x, y))%mod;
if (y > m) sum= (sum+query(rc, x, y))%mod;
return sum;
}
signed main() {
cin >> n >> q>> mod;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
build(1, 1, n);
while (q--) {
int op; cin >> op;
if (op == 2) {
int l, r, k;
cin >> l >> r >> k;
update(1, l, r, k);
}
if(op==1){
int l, r, k;
cin >> l >> r >> k;
update1(1, l, r, k);
}
if (op == 3) {
int l, r;
cin >> l >> r;
int sum = query(1, l, r);
cout << sum << endl;
}
}
return 0;
}
二十九.字典树
代码模板
const int N=100010;
int n;
char s[N];
int ch[N][26],cnt[N],idx;
void insert(char *s){
int p=0;
for(int i=0; s[i]; i ++){
int j=s[i]-'a';//字母映射
if(!ch[p][j])ch[p][j]=++idx;
p=ch[p][j];
}
cnt[p]++;//插入次数
}
int query(char *s){
int p=0;
for(int i=0; s[i]; i ++){
int j=s[i]-'a';
if(!ch[p][j]) return 0;
p=ch[p][j];
}
return cnt[p];
}
例题:P8306 【模板】字典树
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=3000010;
int t,n,q;
char s[N];
int ch[N][62],cnt[N],idx;
int getnum(char x){
if(x>='A'&&x<='Z')
return x-'A';
else if(x>='a'&&x<='z')
return x-'a'+26;
else
return x-'0'+52;
}
void insert(string &s) {
int p=0,len=s.size();
for (int i=0;i<len;i++) {
int j=getnum(s[i]);
if (!ch[p][j]) ch[p][j] = ++idx;
p = ch[p][j];
cnt[p]++;
}
}
int query(string &s) {
int p=0,len=s.size();
for (int i=0;i<len;i++) {
int j=getnum(s[i]);
if (!ch[p][j]) return 0;
p = ch[p][j];
}
return cnt[p];
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> t;
while (t--) {
for(int i=0;i<=idx;i++)
for(int j=0;j<=122;j++)
ch[i][j]=0;
for(int i=0;i<=idx;i++)
cnt[i]=0;
idx = 0;
int n, q;
cin >> n >> q;
for (int i = 0; i < n; i++) {
string ss;
cin >> ss;
insert(ss);
}
for (int i = 0; i < q; i++) {
string ss;
cin >> ss;
cout << query(ss) << endl;
}
}
return 0;
}
三十.ac自动机