CSP 考前膜拜模版

2271 字
11 分钟
CSP 考前膜拜模版

0. 重要提示#

0.1 重定向输入输出#

int main(){
freopen("decode.in","r",stdin);
freopen("decode.out","w",stdout);
// ...
return 0;
}

0.2 文件目录#

截屏2023-10-21 08.58.11.png
截屏2023-10-21 08.58.11.png

截屏2023-10-21 08.58.46.png
截屏2023-10-21 08.58.46.png

0.3 试题#

CSP-J 2023

1. P3865 ST 表#

题目描述#

给定一个长度为 NN 的数列,和 MM 次询问,求出每一次询问的区间内数字的最大值。

输入格式#

第一行包含两个整数 N,MN,M,分别表示数列的长度和询问的个数。

第二行包含 NN 个整数(记为 aia_i),依次表示数列的第 ii 项。

接下来 MM 行,每行包含两个整数 li,ril_i,r_i,表示查询的区间为 [li,ri][l_i,r_i]

输出格式#

输出包含 MM 行,每行一个整数,依次表示每一次询问的结果。

样例 #1#

样例输入 #1#

8 8
9 3 1 7 5 6 0 8
1 6
1 5
2 7
2 6
1 8
4 8
3 7
1 8

样例输出 #1#

9
9
7
7
9
8
7
9

提示#

对于 30%30\% 的数据,满足 1N,M101\le N,M\le 10

对于 70%70\% 的数据,满足 1N,M1051\le N,M\le {10}^5

对于 100%100\% 的数据,满足 1N1051\le N\le {10}^51M2×1061\le M\le 2\times{10}^6ai[0,109]a_i\in[0,{10}^9]1liriN1\le l_i\le r_i\le N

代码#

#include <bits/stdc++.h>
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
return x*f;
}
const int maxn=1e6+5;
int n,m;
int a[maxn];
int st[maxn][(int)log2(maxn)+1];
int l,r;
int solve(int l,int r){
return max(st[l][(int)log2(r-l+1)],st[r-(1<<int(log2(r-l+1)))+1][(int)log2(r-l+1)]);
}
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++){
a[i]=read();
st[i][0]=a[i];
}
for(int j=1;j<=log2(n);j++){
for(int i=1;i+(1<<(j-1))<=n;i++){
st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
}
while(m--){
l=read(),r=read();
cout<<solve(l,r)<<"\n";
}
return 0;
}

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

题目背景#

2018 年 7 月 19 日,某位同学在 NOI Day 1 T1 归程 一题里非常熟练地使用了一个广为人知的算法求最短路。

然后呢?

10060100 \rightarrow 60

AgCu\text{Ag} \rightarrow \text{Cu}

最终,他因此没能与理想的大学达成契约。

小 F 衷心祝愿大家不再重蹈覆辙。

题目描述#

给定一个 nn 个点,mm 条有向边的带非负权图,请你计算从 ss 出发,到每个点的距离。

数据保证你能从 ss 出发到任意点。

输入格式#

第一行为三个正整数 n,m,sn, m, s。 第二行起 mm 行,每行三个非负整数 ui,vi,wiu_i, v_i, w_i,表示从 uiu_iviv_i 有一条权值为 wiw_i 的有向边。

输出格式#

输出一行 nn 个空格分隔的非负整数,表示 ss 到每个点的距离。

样例 #1#

样例输入 #1#

4 6 1
1 2 2
2 3 2
2 4 1
1 3 5
3 4 3
1 4 4

样例输出 #1#

0 2 4 3

提示#

样例解释请参考 数据随机的模板题

1n1051 \leq n \leq 10^5

1m2×1051 \leq m \leq 2\times 10^5

s=1s = 1

1ui,vin1 \leq u_i, v_i\leq n

0wi1090 \leq w_i \leq 10 ^ 9,

0wi1090 \leq \sum w_i \leq 10 ^ 9

本题数据可能会持续更新,但不会重测,望周知。

代码#

#include <bits/stdc++.h>
using namespace std;
int n,m,s;
struct node{
int a,val;
bool operator < (node b) const{
return val>b.val;
}
};
vector <node> G[100010];
priority_queue <node> q;
int dis[100010];
int main(){
cin>>n>>m>>s;
int a,b,c;
for(int i=1;i<=m;i++){
cin>>a>>b>>c;
G[a].push_back({b,c});
}
q.push({s,0});
memset(dis,0x3f,sizeof dis);
dis[s]=0;
while(!q.empty()){
node now=q.top();
q.pop();
if(now.val!=dis[now.a])continue;
for(int i=0;i<G[now.a].size();i++){
node nxt=G[now.a][i];
if(dis[nxt.a]>dis[now.a]+nxt.val){
q.push({nxt.a,dis[now.a]+nxt.val});
dis[nxt.a]=now.val+nxt.val;
}
}
}
for(int i=1;i<=n;i++){
cout<<dis[i]<<" ";
}
return 0;
}

3. P3366【模板】最小生成树#

题目描述#

如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出 orz

输入格式#

第一行包含两个整数 N,MN,M,表示该图共有 NN 个结点和 MM 条无向边。

接下来 MM 行每行包含三个整数 Xi,Yi,ZiX_i,Y_i,Z_i,表示有一条长度为 ZiZ_i 的无向边连接结点 Xi,YiX_i,Y_i

输出格式#

如果该图连通,则输出一个整数表示最小生成树的各边的长度之和。如果该图不连通则输出 orz

样例 #1#

样例输入 #1#

4 5
1 2 2
1 3 2
1 4 3
2 3 4
3 4 3

样例输出 #1#

7

提示#

数据规模:

对于 20%20\% 的数据,N5N\le 5M20M\le 20

对于 40%40\% 的数据,N50N\le 50M2500M\le 2500

对于 70%70\% 的数据,N500N\le 500M104M\le 10^4

对于 100%100\% 的数据:1N50001\le N\le 50001M2×1051\le M\le 2\times 10^51Zi1041\le Z_i \le 10^4

样例解释:

所以最小生成树的总边权为 2+2+3=72+2+3=7

代码#

#include <bits/stdc++.h>
using namespace std;
int n,m;
int x,y,z;
const int maxn=5010;
struct ed{
int val,x,y;
};
vector <ed> edges;
bool cmp(ed x,ed y){
return x.val<y.val;
}
int numberOfEdges=0;
int fa[maxn];
int find(int x){
return fa[x]==x?x:fa[x]=find(fa[x]);
}
void merge(int x,int y){
int fax=find(x),fay=find(y);
if(fax!=fay){
fa[fax]=fay;
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
fa[i]=i;
}
for(int i=1;i<=m;i++){
cin>>x>>y>>z;
if(x==y)continue;
edges.push_back({z,x,y});
}
int ans=0;
sort(edges.begin(),edges.end(),cmp);
for(auto i:edges){
int fax=find(i.x),fay=find(i.y);
if(fax!=fay){
numberOfEdges++;
merge(i.x,i.y);
ans+=i.val;
}
}
if(numberOfEdges!=n-1){
cout<<"orz"<<endl;
return 0;
}
cout<<ans;
return 0;
}

4. 【模板】树状数组 1#

题目描述#

如题,已知一个数列,你需要进行下面两种操作:

  • 将某一个数加上 xx

  • 求出某区间每一个数的和

输入格式#

第一行包含两个正整数 n,mn,m,分别表示该数列数字的个数和操作的总个数。

第二行包含 nn 个用空格分隔的整数,其中第 ii 个数字表示数列第 ii 项的初始值。

接下来 mm 行每行包含 33 个整数,表示一个操作,具体如下:

  • 1 x k 含义:将第 xx 个数加上 kk

  • 2 x y 含义:输出区间 [x,y][x,y] 内每个数的和

输出格式#

输出包含若干行整数,即为所有操作 22 的结果。

样例 #1#

样例输入 #1#

5 5
1 5 4 2 3
1 1 3
2 2 5
1 3 -1
1 4 2
2 1 4

样例输出 #1#

14
16

提示#

【数据范围】

对于 30%30\% 的数据,1n81 \le n \le 81m101\le m \le 10
对于 70%70\% 的数据,1n,m1041\le n,m \le 10^4
对于 100%100\% 的数据,1n,m5×1051\le n,m \le 5\times 10^5

数据保证对于任意时刻,aa 的任意子区间(包括长度为 11nn 的子区间)和均在 [231,231)[-2^{31}, 2^{31}) 范围内。

样例说明:

故输出结果14、16

代码#

#include<cstdio>
#include<iostream>
using namespace std;
const int maxn=5e5+10;
int n,m;
int a[maxn];
int bit[maxn];
int k,x,y;
int lowbit(int x){
return x & -x;
}
void change(int x,int y){
while(x<=n){
bit[x]+=y;
x+=lowbit(x);
}
}
int query(int x){
int ans=0;
while(x>0){//边界条件x>0!
ans+=bit[x];
x-=lowbit(x);
}
return ans;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
change(i,a[i]);//一开始都是0,要初始化加上a[i]
}
for(int i=1;i<=m;i++){
scanf("%d%d%d",&k,&x,&y);
if(k==1){
change(x,y);//将第x个数加上y
}else{
printf("%d\n",query(y)-query(x-1));//求出区间和
}
}
return 0;
}

5. 【模板】树状数组 2#

题目描述#

如题,已知一个数列,你需要进行下面两种操作:

  1. 将某区间每一个数加上 xx

  2. 求出某一个数的值。

输入格式#

第一行包含两个整数 NNMM,分别表示该数列数字的个数和操作的总个数。

第二行包含 NN 个用空格分隔的整数,其中第 ii 个数字表示数列第 ii 项的初始值。

接下来 MM 行每行包含 2244个整数,表示一个操作,具体如下:

操作 11: 格式:1 x y k 含义:将区间 [x,y][x,y] 内每个数加上 kk

操作 22: 格式:2 x 含义:输出第 xx 个数的值。

输出格式#

输出包含若干行整数,即为所有操作 22 的结果。

样例 #1#

样例输入 #1#

5 5
1 5 4 2 3
1 2 4 2
2 3
1 1 5 -1
1 3 5 7
2 4

样例输出 #1#

6
10

提示#

样例 1 解释:#

故输出结果为 6、10。


数据规模与约定#

对于 30%30\% 的数据:N8N\le8M10M\le10

对于 70%70\% 的数据:N10000N\le 10000M10000M\le10000

对于 100%100\% 的数据:1N,M5000001 \leq N, M\le 5000001x,yn1 \leq x, y \leq n,保证任意时刻序列中任意元素的绝对值都不大于 2302^{30}

代码#

#include <bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
int a[maxn];
int bit[maxn];
int n,m;
int cmd,x,y,k;
// 差分思想,用bit[x]表示第i个数
int lowbit(int x){
return x&-x;
}
void add(int x,int y){
while(x<=n){
bit[x]+=y;
x+=lowbit(x);
}
}
int query(int x){
int ans=0;
while(x>0){//边界条件x>0!
ans+=bit[x];
x-=lowbit(x);
}
return ans;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
add(i,a[i]);
add(i+1,-a[i]);
}
for(int i=1;i<=m;i++){
cin>>cmd;
if(cmd==1){
cin>>x>>y>>k;
add(x,k);
add(y+1,-k);
}else{
cin>>x;
cout<<query(x)<<endl;
}
}
return 0;
}

6. 【模板】线段树 1#

题目描述#

如题,已知一个数列,你需要进行下面两种操作:

  1. 将某区间每一个数加上 kk
  2. 求出某区间每一个数的和。

输入格式#

第一行包含两个整数 n,mn, m,分别表示该数列数字的个数和操作的总个数。

第二行包含 nn 个用空格分隔的整数,其中第 ii 个数字表示数列第 ii 项的初始值。

接下来 mm 行每行包含 3344 个整数,表示一个操作,具体如下:

  1. 1 x y k:将区间 [x,y][x, y] 内每个数加上 kk
  2. 2 x y:输出区间 [x,y][x, y] 内每个数的和。

输出格式#

输出包含若干行整数,即为所有操作 2 的结果。

样例 #1#

样例输入 #1#

5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4

样例输出 #1#

11
8
20

提示#

对于 30%30\% 的数据:n8n \le 8m10m \le 10
对于 70%70\% 的数据:n103n \le {10}^3m104m \le {10}^4
对于 100%100\% 的数据:1n,m1051 \le n, m \le {10}^5

保证任意时刻数列中所有元素的绝对值之和 1018\le {10}^{18}

【样例解释】

代码#

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=8e5+10;
int tree[maxn];
int lazy[maxn];
int n,m;
void push_up(int p){
tree[p]=tree[p*2]+tree[p*2+1];
}
void push_down(int p,int l,int r){
lazy[p*2]+=lazy[p];
lazy[p*2+1]+=lazy[p];
int mid=(r+l)/2;
tree[p*2]+=lazy[p]*(mid-l+1);
tree[p*2+1]+=lazy[p]*(r-mid);
lazy[p]=0;
}
void build(int s,int t,int p){
if(s==t){
cin>>tree[p];
return;
}
int mid=(s+t)/2;
build(s,mid,p*2);
build(mid+1,t,p*2+1);
push_up(p);
}
void update(int l,int r,int s,int t,int p,int v){
if(l<=s&&t<=r){
lazy[p]+=v;
tree[p]+=v*(t-s+1);
return;
}
push_down(p,s,t);
int mid=(s+t)/2;
if(mid>=l)
update(l,r,s,mid,p*2,v);
if(mid<r)
update(l,r,mid+1,t,p*2+1,v);
push_up(p);
}
int query(int l,int r,int s,int t,int p){
if(l<=s&&t<=r){
return tree[p];
}
int mid=(s+t)/2;
int ans=0;
push_down(p,s,t);
if(mid>=l){
ans+=query(l,r,s,mid,p*2);
}
if(mid<r){
ans+=query(l,r,mid+1,t,p*2+1);
}
return ans;
}
signed main(){
cin>>n>>m;
build(1,n,1);
while(m--){
int op,x,y,z;
cin>>op;
if(op==1){
cin>>x>>y>>z;
update(x,y,1,n,1,z);
}else{
cin>>x>>y;
cout<<query(x,y,1,n,1)<<endl;
}
}
return 0;
}

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!

赞助
CSP 考前膜拜模版
https://www.0x3f.foo/posts/考前膜拜模版/
作者
Dignite
发布于
2023-10-20
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
Dignite
When nothing goes right, go left.
公告
欢迎来到我的博客!这是一则示例公告。
分类
标签
站点统计
文章
146
分类
5
标签
271
总字数
314,753
运行时长
0
最后活动
0 天前

目录