这里的所有题目,均来源于 洛谷.
一些没有 阐明主要思想 的,博主会慢慢更的。
主要功能是 供博主快速查询板子,给大家提供一个优质的模板库。
P3367 【模板】并查集
简要题意:维护连通块的合并与查询。
数据结构:并查集。
时间复杂度: O ( n ) \mathcal{O}(n) O(n).
struct BIT {
const int N=2e5+1;
int f[N];
inline int find(int x) {return f[x]==x?x:f[x]=find(f[x]);}
inline void ins(int x,int y) { //合并操作
f[find(x)]=find(y);
}
inline bool ask(int x,int y) { //查询操作
return find(x) == find(y);
}
}
P1226 【模板】快速幂||取余运算
简要题意:求 x y m o d p x^y \mod p xymodp.
板子。
时间复杂度: O ( log p ) O(\log p) O(logp).
typedef long long ll;
inline ll pw(ll x,ll y,ll MOD) {
ll ans=1; while(y) {
if(y&1) ans=ans*x%MOD;
x=x*x%MOD; y>>=1;
}
}
P3371 【模板】单源最短路径(弱化版)
简要题意:给定一个图,求 s s s 到各点的最短路。
算法: SPFA \text{SPFA} SPFA.
时间复杂度: O ( n 2 ) O(n^2) O(n2).
const int N=2e5+1;
int n,m,s,dis[N];
vector<pair<int,int> >G[N];
bool vis[N];
inline void SPFA() {
queue<int>q; memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++) dis[i]=INT_MAX;
q.push(s); dis[s]=0; vis[s]=1;
while(!q.empty()) {
int u=q.front();
q.pop(); vis[u]=0;
for(int i=0;i<G[u].size();i++) {
int v=G[u][i].first,z=G[u][i].second;
if(dis[v]>dis[u]+z) {
dis[v]=dis[u]+z;
if(!vis[v]) {vis[v]=1; q.push(v);}
}
}
}
}
P4779 【模板】单源最短路径(标准版)
简要题意:同弱化版。
算法: Dijkstra \text{Dijkstra} Dijkstra + 堆优化。
时间复杂度: O ( n log n ) \mathcal{O}(n \log n) O(nlogn).
const int N=2e5+1;
int n,m,s,dis[N]; bool vis[N]; vector<pair<int,int> > G[N];
priority_queue<pair<int,int> , vector<pair<int,int> > , greater<pair<int,int> > > q;
inline void Dijkstra(int s) {
dis[s]=0; q.push(make_pair(0,s));
while(!q.empty()) {
int x=q.top().second; q.pop();
if(vis[x]) continue; vis[x]=1;
for(int i=0;i<G[x].size();i++) {
int y=G[x][i].first,w=G[x][i].second;
if(dis[x]+w<dis[y]) {
dis[y]=dis[x]+w;
if(!vis[y]) q.push(make_pair(dis[y],y));
}
}
}
}
P3383 【模板】线性筛素数
注:这题经过一次变换。
简要题意:若干查询第 k k k 小素数。
板子。
时间复杂度: O ( n ) O(n) O(n).
const int N=1e8+1;
const int SN=1e6+1;
bool h[N]; int cnt=0;
int n,m,prime[SN];
inline void Euler() {
for(int i=2;i<=n;i++) {
if(!h[i]) prime[++cnt]=i;
for(int j=1;j<=cnt && i*prime[j]<=n;j++) {
h[i*prime[j]]=1;
if(i%prime[j]==0) break;
}
}
}
P3366 【模板】最小生成树
简要题意:求 最小生成树。
算法: kruskal \text{kruskal} kruskal.
时间复杂度: O ( n m + m log m ) \mathcal{O}(nm + m \log m) O(nm+mlogm).
const int N=2e5+1;
struct tree{
int start,end;
int len;
} a[N];
int n,m,s=0,p=0;
bool h[N],vis[N];
vector<pair<int,int> >G[N];
inline bool cmp(tree x,tree y){return x.len<y.len;}
inline void dfs(int dep) {
if(vis[dep]) return;
vis[dep]=1; p++;
for(int i=0;i<G[dep].size();i++) dfs(G[dep][i].first);
}
inline bool check(){
dfs(1);
if(p==n) return 0;
return 1;
}
int main(){
n=read(),m=read();
for(int i=1;i<=m;i++) {
a[i].start=read(); a[i].end=read();
a[i].len=read();
G[a[i].start].push_back(make_pair(a[i].end,a[i].len));
}
if(check()) {printf("orz\n");return 0;}
sort(a+1,a+1+m,cmp);
s=a[1].len; h[a[1].start]=h[a[1].end]=1;
for(int p=2;p<n;p++)
for(int i=2;i<=m;i++)
if(h[a[i].start]+h[a[i].end]==1) {
s+=a[i].len;
h[a[i].start]=h[a[i].end]=1;
break;
}
printf("%d\n",s);
return 0;
}
P1886 滑动窗口 /【模板】单调队列
简要题意:维护滑动窗口内的极值。
算法:线段树 单调队列优化。
时间复杂度: O ( n ) \mathcal{O}(n) O(n).
const int N=2e5+1;
int n,a[N];
deque<int> q;
for(int i=1;i<=n;i++) {
while(!q.empty() && a[q.back()]>=a[i]) q.pop_back();
q.push_back(i);
while(!q.empty() && q.front()+k<=i) q.pop_front();
if(i>=k) write(a[q.front()]),putchar(' ');
} puts(""); q.clear();
for(int i=1;i<=n;i++) {
while(!q.empty() && a[q.back()]<=a[i]) q.pop_back();
q.push_back(i);
while(!q.empty() && q.front()+k<=i) q.pop_front();
if(i>=k) write(a[q.front()]),putchar(' ');
}
P3374 【模板】树状数组 1
简要题意:维护树状数组。
数据结构:树状数组。
时间复杂度: O ( n log n + m log n ) \mathcal{O}(n \log n + m \log n) O(nlogn+mlogn).
typedef long long ll;
const int N=1e6+1;
ll a[N],c[N];
struct BIT {
inline int lowbit(int x) {return x & (-x);}
inline ll sum(int x) {
ll s=0; while(x>0) {
s+=c[x];
x-=lowbit(x);
} return s;
}
inline void update(int x,int k) {
a[x]+=k; while(x<=n) {
c[x]+=k; x+=lowbit(x);
}
}
inline ll query(int l,int r) {
return sum(r)-sum(x-1);
}
} ;
P3811 【模板】乘法逆元
简要题意:维护 1 1 1 ~ n n n 所有数关于质数 p p p 的逆元。
算法:公式递推。
时间复杂度: O ( n ) \mathcal{O}(n) O(n).
const int N=2e6+1;
int inv[N],n,MOD;
inv[1]=1;
for(int i=2;i<=n;i++) inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD;
P3379 【模板】最近公共祖先(LCA)
简要题意:给定一棵树,维护最近公共祖先。
算法:倍增。
时间复杂度: O ( n log n + m log n ) \mathcal{O}(n \log n + m \log n) O(nlogn+mlogn).
const int N=5e5+1;
int n,m,root,fa[N];
vector<int> G[N];
int f[N][21],dep[N];
inline void dfs(int u,int fa) {
dep[u]=dep[fa]+1; f[u][0]=fa;
for(int i=1;i<=20;i++) f[u][i]=f[f[u][i-1]][i-1];
for(int i=0;i<G[u].size();i++) {
int v=G[u][i]; if(v==fa) continue;
dfs(v,u);
}
}
inline int LCA(int x,int y) {
if(dep[x]>dep[y]) swap(x,y);
for(int i=20;i>=0;i--)
if(dep[x]<=dep[y]-(1<<i)) y=f[y][i];
if(x==y) return x;
for(int i=20;i>=0;i--) {
if(f[x][i]==f[y][i]) continue;
else x=f[x][i],y=f[y][i];
} return f[x][0];
}
P3368 【模板】树状数组 2
简要题意:维护区间加,单点查。
数据结构:树状数组。
时间复杂度: O ( n log n + m log n ) \mathcal{O}(n \log n + m \log n) O(nlogn+mlogn).
随便 树状数组 维护一下就行了呗。
const int N=1e6+1;
typedef long long ll;
int a[N]; ll c[N];
int n,m,x,y;
struct BIT {
inline int lowbit(int x) {return x & (-x);}
inline ll sum(int x) {
ll s=0; while(x>0) {
s+=c[x];
x-=lowbit(x);
} return s;
}
inline void update(int x,int k) {
while(x<=n) {
c[x]+=k;
x+=lowbit(x);
}
}
} ;
P3372 【模板】线段树 1
简要题意:维护线段树。
数据结构:线段树。
时间复杂度: O ( n log n + m log n ) \mathcal{O}(n \log n + m \log n) O(nlogn+mlogn).
#define int long long
const int N=1e5+1;
#define L (i<<1)
#define R (i<<1)+1
struct BIT {
inline void update(int x) {
t[x].sum=t[L].sum+t[R].sum;
}
inline void build_tree(int x,int l,int r) {
t[x].l=l; t[x].r=r; t[x].tag=0;
if(l==r) {t[x].sum=a[l]; return;}
int mid=(l+r)>>1;
build_tree(L,l,mid); build_tree(R,mid+1,r);
update(x);
}
inline void pushdown(int x) {
int p=t[x].tag; if(!p) return;
t[L].tag+=p; t[R].tag+=p;
t[L].sum+=p*(t[L].r-t[L].l+1);
t[R].sum+=p*(t[R].r-t[R].l+1);
t[x].tag=0;
}
inline void change(int x,int l,int r,int k) {
if(l<=t[x].l && t[x].r<=r) {t[x].tag+=k; t[x].sum+=k*(t[x].r-t[x].l+1);return;}
int mid=(t[x].l+t[x].r)>>1; pushdown(x);
if(l<=mid) change(L,l,r,k);
if(r>mid) change(R,l,r,k);
update(x);
}
inline int ask(int x,int l,int r) {
if(l<=t[x].l && t[x].r<=r) return t[x].sum;
int mid=(t[x].l+t[x].r)>>1,ans=0; pushdown(x);
if(l<=mid) ans+=ask(L,l,r);
if(r>mid) ans+=ask(R,l,r);
return ans;
}
} T;
P3373 【模板】线段树 2
简要题意:维护线段树。
数据结构:线段树。
时间复杂度: O ( n log n + m log n ) \mathcal{O}(n \log n + m \log n) O(nlogn+mlogn).
#define L (i<<1)
#define R (i<<1)+1
typedef long long ll;
const ll N=1e5+1;
ll n,m,MOD;
struct node{
ll l,r,sum;
ll mul,add;
};
node t[4*N]; ll a[N];
struct BIT {
inline void update(ll i) {t[i].sum=(t[L].sum+t[R].sum)%MOD;}
inline void build_tree(ll i,ll l,ll r) {
t[i].l=l; t[i].r=r; t[i].mul=1;
if(l==r) {t[i].sum=a[l]%MOD;return;}
ll mid=(l+r)>>1;
build_tree(L,l,mid);
build_tree(R,mid+1,r);
update(i);
}
inline void pushdown(ll i) {
t[L].sum=(t[i].mul*t[L].sum+((t[L].r-t[L].l+1)*t[i].add)%MOD)%MOD;
t[R].sum=(t[i].mul*t[R].sum+((t[R].r-t[R].l+1)*t[i].add)%MOD)%MOD;
t[L].mul=(t[L].mul*t[i].mul)%MOD;
t[R].mul=(t[R].mul*t[i].mul)%MOD;
t[L].add=(t[i].mul*t[L].add+t[i].add)%MOD;
t[R].add=(t[i].mul*t[R].add+t[i].add)%MOD;
t[i].mul=1; t[i].add=0;
}
inline void change_add(ll i,ll l,ll r,ll k) {
if(l<=t[i].l && t[i].r<=r) {
t[i].add=(t[i].add+k)%MOD;
t[i].sum=(t[i].sum+k*(t[i].r-t[i].l+1))%MOD;
return;
} pushdown(i); update(i);
ll mid=(t[i].l+t[i].r)>>1;
if(l<=mid) change_add(L,l,r,k);
if(r>mid) change_add(R,l,r,k);
update(i);
}
inline void change_mul(ll i,ll l,ll r,ll k) {
if(l<=t[i].l && t[i].r<=r) {
t[i].add=(t[i].add*k)%MOD;
t[i].mul=(t[i].mul*k)%MOD;
t[i].sum=(t[i].sum*k)%MOD; return;
} pushdown(i); update(i);
ll mid=(t[i].l+t[i].r)>>1;
if(l<=mid) change_mul(L,l,r,k);
if(r>mid) change_mul(R,l,r,k);
update(i);
}
inline ll querysum(ll i,ll l,ll r) {
if(l<=t[i].l && t[i].r<=r) return t[i].sum;
pushdown(i);
ll mid=(t[i].l+t[i].r)>>1,ans=0;
if(l<=mid) ans=(ans+querysum(L,l,r))%MOD;
if(r>mid) ans=(ans+querysum(R,l,r))%MOD;
return ans;
}
} ;
P3375 【模板】KMP字符串匹配
简要题意:维护 KMP \text{KMP} KMP.
算法: KMP \text{KMP} KMP.
时间复杂度: O ( n + m ) \mathcal{O}(n+m) O(n+m).
const int N=1e6+1;
char s[N],p[N];
int nxt[N];
inline void getNext() {
nxt[0]=-1; int len=strlen(p);
int j=0,k=-1;
while(j<len)
if(k==-1 || p[j]==p[k]) {
nxt[++j]=++k;
} else k=nxt[k];
}
inline int KMP() {
int i=0,j=0;
int l1=strlen(s),l2=strlen(p);
while(i<l1) {
if(j==-1 || s[i]==p[j]) i++,j++;
else j=nxt[j];
if(j==l2) printf("%d\n",i-l2+1),j=nxt[j];
}
if(j==strlen(p)) return i-j;
else return -1;
}
P1939 【模板】矩阵加速(数列)
利用矩阵加速转移, O ( log n ) \mathcal{O}(\log n) O(logn).
typedef long long ll;
const ll MOD=1e9+7;
struct martix {
ll a[4][4];
} s;
int n,T;
inline martix times(martix x,martix y) {
martix t; memset(t.a,0,sizeof(t.a));
for(int i=1;i<=3;i++) for(int j=1;j<=3;j++)
for(int k=1;k<=3;k++)
t.a[i][j]=(t.a[i][j]+x.a[i][k]*y.a[k][j])%MOD;
return t;
}
inline martix pw(martix x,int n) {
if(n<=1) return x;
martix ans=x; n--;
while(n>0) {
if(n&1) ans=times(ans,x);
n>>=1; x=times(x,x);
} return ans;
}