模板汇总
洛谷的【模板】题
按照难度排序
题单
P3367 \(\color{#F39C11}{【模板】并查集}\)
大意
开始有\(n\)个元素,每个元素初始时在一个单独的集合里。接下来\(m\)次操作,将\(x,y\)所在的集合合并或者判断\(x,y\)是否在一个集合里。
思路
无
代码
#include<bits/stdc++.h>
using namespace std;
int i,j,k,n,m,s,ans,f[10010],p1,p2,p3;
int find(int k){
if(f[k]==k)return k;
return f[k]=find(f[k]);
}
int main()
{
cin>>n>>m;
for(i=1;i<=n;i++)
f[i]=i;
for(i=1;i<=m;i++){
cin>>p1>>p2>>p3;
if(p1==1)
f[find(p2)]=find(p3);
else
if(find(p2)==find(p3))
printf("Y\n");
else
printf("N\n");
}
return 0;
}
P1226 \(\color{#F39C11}{【模板】快速幂||取余运算}\)
大意
给定自然数\(a,b,p\),求\(a^b\bmod p\)。
思路
因为\(a^b\bmod p=\begin{cases}(a^{\left\lfloor\frac{b}{2}\right\rfloor})^2&b\bmod 2 = 0\\(a^{\left\lfloor\frac{b}{2}\right\rfloor})^2 \times a& b\bmod 2 = 1\end{cases}\),考虑递归求解。
代码
#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;
ll a,b,p;
ll qp(ll di,ll ci,ll mod){
if(ci==0){
return 1;
}
ll ans=qp(di,ci/2,mod)%mod;
ans=ans*ans%mod;
if(ci&1){
ans=ans*a%mod;
}
return ans;
}
int main(){
scanf("%lld%lld%lld",&a,&b,&p);
printf("%lld^%lld mod %lld=%lld",a,b,p,qp(a,b,p));
return 0;
}
P3383\(\color{#F39C11}{【模板】线性筛素数}\)
大意
有\(q\)次询问,每次给定整数\(k\),求第\(k\)大的素数(所求素数\(\;\le 10^8\))。
思路
节选自2021CSP-J1

此处\(a,b\)数组即为欧拉筛,\(a\)记录是否被访问,\(b\)记录目前搜到的有素数。
自行搜索欧拉筛
代码
#include<iostream>
#include<cstdio>
#define maxn 100000005
using namespace std;
int n,q,k;
bool vis[maxn];
int prime[maxn],cnt=0;
int main(){
scanf("%d%d",&n,&q);
for(int i=2;i<=n;i++){
if(!vis[i]){
vis[i]=1;
prime[++cnt]=i;
}
for(int j=1;j<=cnt&&i*prime[j]<maxn;j++){
vis[i*prime[j]]=1;
}
}
while(q--){
scanf("%d",&k);
printf("%d\n",prime[k]);
}
return 0;
}
P3366\(\color{#F39C11}{【模板】最小生成树}\)
大意
给定一无向图,输出最小生成树的边权和,若不连通输出\(orz\)。
思路
(\(Kruskal\) 算法)按边权从小到大枚举边,用并查集维护是否已经在最小生成树中。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#define maxn 200005
using namespace std;
int n,m,cnt,ans;
int fa[maxn];
int find(int x){
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
struct node{
int from,to,dis;
}a[maxn];
bool cmp(node a,node b){
return a.dis<b.dis;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
fa[i]=i;
}
for(int i=1;i<=m;i++){
scanf("%d%d%d",&a[i].from,&a[i].to,&a[i].dis);
}
sort(a+1,a+1+m,cmp);
for(int i=1;i<=m;i++){
if(find(a[i].from)!=find(a[i].to)){
fa[find(a[i].from)]=find(a[i].to);
cnt++;
ans+=a[i].dis;
}
if(cnt==n-1){
break;
}
}
if(cnt!=n-1){
printf("orz");
return 0;
}
printf("%d",ans);
return 0;
}
P3378\(\color{#F39C11}{【模板】堆}\)
大意
给定一初始为空的序列,维护数据结构满足以下操作:插入、删除最小数据、输出最小数据。
思路
维护\(STL\)优先队列即可。
代码
#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
int n,opt,x;
priority_queue<int,vector<int>,greater<int> >q;
int main(){
scanf("%d",&n);
while(n--){
scanf("%d",&opt);
if(opt==1){
scanf("%d",&x);q.push(x);
}else if(opt==2){
printf("%d\n",q.top());
}else if(opt==3){
q.pop();
}
}
return 0;
}
P3370\(\color{#F39C11}{【模板】字符串哈希}\)
大意
给定\(n\)个字符串,输出有多少个不同的。
思路
用\(map\)维护即可。
代码
#include<iostream>
#include<cstdio>
#include<map>
using namespace std;
int n,ans;
string a;
map<string,int> mp;
int main(){
scanf("%d",&n);
while(n--){
cin>>a;
if(!mp[a]){
mp[a]=1;
ans++;
}
}
printf("%d",ans);
return 0;
}
P1177\(\color{#F39C11}{【模板】快速排序}\)
大意
输入\(n\)个数,排序后升序输出
思路
无。
代码
#include<iostream>
#include<cstdio>
#define maxn 100005
using namespace std;
int n,a[maxn];
void quicksort(int l,int r){
int i=l,j=r,temp=a[(l+r)/2];
while(i<=j){
while(a[i]<temp){
i++;
}
while(a[j]>temp){
j--;
}
if(i<=j){
swap(a[i],a[j]);
i++;
j--;
}
}
if(l<j) quicksort(l,j);
if(r>i) quicksort(i,r);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
quicksort(1,n);
for(int i=1;i<=n;i++){
printf("%d ",a[i]);
}
return 0;
}
/*
5
3 2 4 5 1
*/
P1886\(\color{#F39C11}{滑动窗口 /【模板】单调队列}\)
大意
有一长\(n\)的序列\(a\),给定正整数\(k\),分别求\(i\)从\(1\)到\(n-k+1\)时\(\left[i,i+k-1\right]\)区间的最小\(\&\)大值。
思路
代码
#include<iostream>
#include<cstdio>
#define maxn 1000005
using namespace std;
int n,k;
int a[maxn];
int q1[maxn],q2[maxn],h1=1,t1=0,h2=1,t2=0;//q1维护最大值,q2维护最小值
int ans1[maxn],ans2[maxn];
void push1(int x,int l){
while(t1>=h1&&a[x]>a[q1[t1]]){
t1--;
}
q1[++t1]=x;
while(t1>=h1&&q1[h1]<l){
h1++;
}
}
void push2(int x,int l){
while(t2>=h2&&a[x]<a[q2[t2]]){
t2--;
}
q2[++t2]=x;
while(t2>=h2&&q2[h2]<l){
h2++;
}
}
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
q1[++t1]=1;
q2[++t2]=1;
for(int i=2;i<=k;i++){
push1(i,-1);
push2(i,-1);
}
ans1[1]=q1[h1];
ans2[1]=q2[h2];
for(int i=k+1;i<=n;i++){
push1(i,i-k+1);
push2(i,i-k+1);
ans1[i-k+1]=q1[h1];
ans2[i-k+1]=q2[h2];
}
for(int i=1;i<=n-k+1;i++){
printf("%d ",a[ans2[i]]);
}
printf("\n");
for(int i=1;i<=n-k+1;i++){
printf("%d ",a[ans1[i]]);
}
return 0;
}
P3382\(\color{#FFC116}{【模板】三分法}\)
大意
给定一函数和区间\([l,r]\),求\(x\)使得函数在\([l,x]\)递增,在\([x,r]\)递减。
思路及相应代码
- 引理:秦九昭算法
用此求函数值
- 三分法
每次找到区间的两个三等分点\(lmid\)和\(rmid\),若\(f(lmid)<f(rmid)\)则在\([lmid,r]\)范围找,否则在\([l,rmid]\)范围找
#include<iostream>
#include<cstdio>
#define maxn 25
#define eps (1e-7)
using namespace std;
int n;
double l,r,lmid,rmid;
double a[maxn];
double f(double x){
double ans=a[n];
for(int i=n-1;i>=0;i--){
ans=ans*x+a[i];
}
return ans;
}
int main(){
scanf("%d%lf%lf",&n,&l,&r);
for(int i=n;i>=0;i--){
scanf("%lf",&a[i]);
}
while(r-l>=eps){
lmid=l+(r-l)/3;
rmid=r-(r-l)/3;
if(f(lmid)<f(rmid)){
l=lmid;
}else{
r=rmid-eps;
}
}
printf("%.5lf",l);
return 0;
}
- 求导\(+\)二分
令原函数\(f\)的导数为\(f'\),所以只需要找\([l,r]\)区间内\(f'\)的零点即可。注意此时我们最好用以下式子:
#include<iostream>
#include<cstdio>
#define maxn 25
#define eps (1e-10)
using namespace std;
int n;
double l,r,mid;
double a[maxn];
double f(double x){
double ans=a[n];
for(int i=n-1;i>=0;i--){
ans=ans*x+a[i];
}
return ans;
}
double derivative(double x){
return (f(x+eps)-f(x))/eps;
}
int main(){
scanf("%d%lf%lf",&n,&l,&r);
for(int i=n;i>=0;i--){
scanf("%lf",&a[i]);
}
while(r-l>eps){
mid=(l+r)/2;
if(derivative(mid)>0) l=mid;
else r=mid;
}
printf("%.5lf",mid);
return 0;
}

浙公网安备 33010602011771号