CSP-S提高组数据结构算法模板大合集
CSP-S 算法总结
2.2.1 基础知识与编程环境
无
2.2.2 C++ 程序设计 2
- set/nultiset
- map/multimap
- deque/priority_queue
- STL
2.2.3 数据结构
P1886 滑动窗口 /【模板】单调队列
#include <iostream>
using namespace std; int n, k, a[1000005];
int q[1000005], h, t; void maxx() {
h = 0; t = -1;
for (int i = 1; i <= n; ++i) {
while (h <= t && a[q[t]] <= a[i]) t--;
q[++t] = i;
if (q[h] <= i - k) h++;
if (i >= k) cout << a[q[h]] << " ";
}
cout << endl;
} void minn() {
h = 0; t = -1;
for (int i = 1; i <= n; ++i) {
while (h <= t && a[q[t]] >= a[i]) t--;
q[++t] = i;
if (q[h] <= i - k) h++;
if (i >= k) cout << a[q[h]] << " ";
}
cout << endl;
} int main() {
cin >> n >> k;
for (int i = 1; i <= n; ++i) cin >> a[i];
minn();
maxx();
return 0;
}P3865 【模板】ST 表
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e6 + 10;
int Max[MAXN][21]; int Query(int l, int r) {
int k = log2(r - l + 1);
return max(Max[l][k], Max[r - (1 << k) + 1][k]);
} int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); int N, M;
cin >> N >> M;
for (int i = 1; i <= N; i++)
cin >> Max[i][0]; for (int j = 1; j <= 21; j++)
for (int i = 1; i + (1 << (j - 1)) <= N; i++)
Max[i][j] = max(Max[i][j - 1], Max[i + (1 << (j - 1))][j - 1]); for (int i = 1; i <= M; i++) {
int l, r;
cin >> l >> r;
cout << Query(l, r) << '\n';
}
return 0;
}P3367 【模板】并查集
#include<bits/stdc++.h>
using namespace std;
int i,j,k,n,m,s,ans,f[10010],p1,p2,p3;
//f[i]表示i的集合名
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;//初始化i的老大为自己
for(i=1;i<=m;i++){
cin>>p1>>p2>>p3;
if(p1==1)
f[find(p2)]=find(p3);
//p3打赢了p2
else
if(find(p2)==find(p3))
//是否是一伙的
printf("Y\n");
else
printf("N\n");
}
return 0;
}P3374【模板】树状数组 1
#include <bits/stdc++.h>
using namespace std;
int n,m,tree[2000010];
int lowbit(int k) { return k & -k; }
void add(int x,int k) {
while(x<=n) {
tree[x]+=k;
x+=lowbit(x);
}
}
int sum(int x) {
int ans=0;
while(x!=0) {
ans+=tree[x];
x-=lowbit(x);
}
return ans;
}
int main() {
cin>>n>>m;
for(int i=1; i<=n; i++) {
int a;
cin>>a;
add(i,a);
}
for(int i=1; i<=m; i++) {
int a,b,c;
cin>>a>>b>>c;
if(a==1)
add(b,c);
if(a==2)
cout<<sum(c)-sum(b-1)<<endl;
}
}P3372 【模板】线段树 1
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define maxn 1000005
ll a[maxn],w[maxn*4],lzy[maxn*4]; void pushup(int u){w[u]=w[u*2]+w[u*2+1];} void build(int u,int L,int R){ //线段树建立
if(L==R){w[u]=a[L];return;}
int M=L+R>>1;
build(u*2,L,M);
build(u*2+1,M+1,R);
pushup(u);
} ll query1(int u,int L,int R,int p){ //单点查询
if(L==R) return w[u];
else {
int M=L+R>>1;
if(M>=p) return query1(u*2,L,M,p);
else return query1(u*2+1,M+1,R,p);
}
} void update1(int u,int L,int R,int p,ll x){ //单点修改
if(L==R) w[u]=x;
else{
int M=L+R>>1;
if(M>=p) update1(u*2,L,M,p,x);
else update1(u*2+1,M+1,R,p,x);
pushup(u);
}
} bool InRange(int L,int R,int l,int r){return ((l<=L)&&(R<=r));} //判断 [L,R] 是否被 [l,r] 包含
bool OutoRange(int L,int R,int l,int r){ return ((L>r)||(R<l));} //判断 [L,R] 是否和 [l,r] 无交 void maketag(int u,int len,int x){
lzy[u]+=x;
w[u]+=len*x;
} void pushdown(int u,int L,int R){
int M=L+R>>1;
maketag(u*2,M-L+1,lzy[u]);
maketag(u*2+1,R-M,lzy[u]);
lzy[u]=0;
}
ll query(int u,int L,int R,int l,int r){
if(InRange(L,R,l,r)){return w[u];}
else if(!OutoRange(L,R,l,r)){
int M=L+R>>1;
pushdown(u,L,R);
return query(u*2,L,M,l,r)+query(u*2+1,M+1,R,l,r);
}
else return 0;
} void update(int u,int L,int R,int l,int r,ll x){
if(InRange(L,R,l,r)){maketag(u,R-L+1,x);} else if(!OutoRange(L,R,l,r)){
int M=L+R>>1;
pushdown(u,L,R);
update(u*2,L,M,l,r,x);
update(u*2+1,M+1,R,l,r,x);
pushup(u);
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(NULL);cout.tie(NULL); int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){cin>>a[i];}
build(1,1,n);
for(int i=1;i<=m;i++){
int op,x,y;
cin>>op;
ll k;
if(op==1){
cin>>x>>y>>k;
update(1,1,n,x,y,k);
}
else{
cin>>x>>y;
cout<<query(1,1,n,x,y)<<endl;
}
}
return 0;
}
P8306 【模板】字典树
#include<bits/stdc++.h>
using namespace std;
int T,q,n,t[3000005][65],cnt[3000005],idx;
char s[3000005];
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(char str[]){
int p=0,len=strlen(str);
for(int i=0;i<len;i++){
int c=getnum(str[i]);
if(!t[p][c])
t[p][c]=++idx;
p=t[p][c];
cnt[p]++;
}
}
int find(char str[]){
int p=0,len=strlen(str);
for(int i=0;i<len;i++){
int c=getnum(str[i]);
if(!t[p][c])
return 0;
p=t[p][c];
}
return cnt[p];
}
int main(){
scanf("%d",&T);
while(T--){
for(int i=0;i<=idx;i++)
for(int j=0;j<=122;j++)
t[i][j]=0;
for(int i=0;i<=idx;i++)
cnt[i]=0;
idx=0;
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++){
scanf("%s",s);
insert(s);
}
for(int i=1;i<=q;i++){
scanf("%s",s);
printf("%d\n",find(s));
}
}
return 0;
}
P3369 【模板】普通平衡树
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm> using namespace std; const int N = 100010, INF = 1e8; int n;
struct Node
{
int l, r;
int key, val;
int cnt, size;
}tr[N]; int root, idx; void pushup(int p)
{
tr[p].size = tr[tr[p].l].size + tr[tr[p].r].size + tr[p].cnt;
} int get_node(int key)
{
tr[ ++ idx].key = key;
tr[idx].val = rand();
tr[idx].cnt = tr[idx].size = 1;
return idx;
} void zig(int &p) // 右旋
{
int q = tr[p].l;
tr[p].l = tr[q].r, tr[q].r = p, p = q;
pushup(tr[p].r), pushup(p);
} void zag(int &p) // 左旋
{
int q = tr[p].r;
tr[p].r = tr[q].l, tr[q].l = p, p = q;
pushup(tr[p].l), pushup(p);
} void build()
{
get_node(-INF), get_node(INF);
root = 1, tr[1].r = 2;
pushup(root); if (tr[1].val < tr[2].val) zag(root);
} void insert(int &p, int key)
{
if (!p) p = get_node(key);
else if (tr[p].key == key) tr[p].cnt ++ ;
else if (tr[p].key > key)
{
insert(tr[p].l, key);
if (tr[tr[p].l].val > tr[p].val) zig(p);
}
else
{
insert(tr[p].r, key);
if (tr[tr[p].r].val > tr[p].val) zag(p);
}
pushup(p);
} void remove(int &p, int key)
{
if (!p) return;
if (tr[p].key == key)
{
if (tr[p].cnt > 1) tr[p].cnt -- ;
else if (tr[p].l || tr[p].r)
{
if (!tr[p].r || tr[tr[p].l].val > tr[tr[p].r].val)
{
zig(p);
remove(tr[p].r, key);
}
else
{
zag(p);
remove(tr[p].l, key);
}
}
else p = 0;
}
else if (tr[p].key > key) remove(tr[p].l, key);
else remove(tr[p].r, key); pushup(p);
} int get_rank_by_key(int p, int key) // 通过数值找排名
{
if (!p) return 1; // 本题中不会发生此情况
if (tr[p].key == key) return tr[tr[p].l].size + 1;
if (tr[p].key > key) return get_rank_by_key(tr[p].l, key);
return tr[tr[p].l].size + tr[p].cnt + get_rank_by_key(tr[p].r, key);
} int get_key_by_rank(int p, int rank) // 通过排名找数值
{
if (!p) return INF; // 本题中不会发生此情况
if (tr[tr[p].l].size >= rank) return get_key_by_rank(tr[p].l, rank);
if (tr[tr[p].l].size + tr[p].cnt >= rank) return tr[p].key;
return get_key_by_rank(tr[p].r, rank - tr[tr[p].l].size - tr[p].cnt);
} int get_prev(int p, int key) // 找到严格小于key的最大数
{
if (!p) return -INF;
if (tr[p].key >= key) return get_prev(tr[p].l, key);
return max(tr[p].key, get_prev(tr[p].r, key));
} int get_next(int p, int key) // 找到严格大于key的最小数
{
if (!p) return INF;
if (tr[p].key <= key) return get_next(tr[p].r, key);
return min(tr[p].key, get_next(tr[p].l, key));
} int main()
{
build(); scanf("%d", &n);
while (n -- )
{
int opt, x;
scanf("%d%d", &opt, &x);
if (opt == 1) insert(root, x);
else if (opt == 2) remove(root, x);
else if (opt == 3) printf("%d\n", get_rank_by_key(root, x) - 1);
else if (opt == 4) printf("%d\n", get_key_by_rank(root, x + 1 ));
else if (opt == 5) printf("%d\n", get_prev(root, x));
else printf("%d\n", get_next(root, x));
} return 0;
}
P3370 【模板】字符串哈希
#include<bits/stdc++.h>
using namespace std;
set<string> a;
int main(){
string p;
int n,i;
cin>>n;
for(i=0;i<n;i++){
cin>>p;
a.insert(p);
}
cout<<a.size()<<endl;
return 0;
}
2.2.4 算法
离散化
// arr[i] 为初始数组,下标范围为 [1, n]
for (int i = 1; i <= n; ++i) // step 1
tmp[i] = arr[i];
std::sort(tmp + 1, tmp + n + 1); // step 2
int len = std::unique(tmp + 1, tmp + n + 1) - (tmp + 1); // step 3
for (int i = 1; i <= n; ++i) // step 4
arr[i] = std::lower_bound(tmp + 1, tmp + len + 1, arr[i]) - tmp; // std::vector<int> arr;
std::vector<int> tmp(arr); // tmp 是 arr 的一个副本
std::sort(tmp.begin(), tmp.end());
tmp.erase(std::unique(tmp.begin(), tmp.end()), tmp.end());
for (int i = 0; i < n; ++i)
arr[i] = std::lower_bound(tmp.begin(), tmp.end(), arr[i]) - tmp.begin();分治
基数排序
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int n, a[N], tot;
pair<int, int> p[N];
vector<pair<int, int>> G[N];
int main(){
cin >> n;
for (int i = 1; i <= n; i++){
cin >> a[i];
p[i].first = a[i] >> 16,p[i].second = a[i] % 65536;
}
for (int i = 1; i <= n; i++)
G[p[i].second].push_back(p[i]);
for (int i = 0; i < 65536; i++)
for (int j = 0; j < G[i].size(); j++)
p[++tot] = G[i][j];
for (int i = 0; i < 65536; i++)
G[i].clear();
for (int i = 1; i <= n; i++)
G[p[i].first].push_back(p[i]);
tot = 0;
for (int i = 0; i < 65536; i++)
for (int j = 0; j < G[i].size(); j++)
p[++tot] = G[i][j];
for (int i = 1; i <= n; i++)cout <<( (p[i].first << 16) | p[i].second )<< " ";
return 0;
}
归并排序
#include <iostream>
using namespace std;
const int N=1e6+10;
int q[N],tmp[N],n;
void MergeSort(int q[],int l,int r){
if(l>=r) return ;
int mid=(r+l)>>1;
MergeSort(q,l,mid);MergeSort(q,mid+1,r);
int i=l,j=mid+1,k=0;
while(i<=mid&&j<=r){
if(q[i]<=q[j]) tmp[k++]=q[i++];
else tmp[k++]=q[j++];
}
while(i<=mid) tmp[k++]=q[i++];
while(j<=r) tmp[k++]=q[j++];
for(int i=l,j=0;i<=r;i++) q[i]=tmp[j++];
}
int main(){
cin>>n;
for(int i=0;i<n;i++) cin>>q[i];
MergeSort(q,0,n-1);
for(int i=0;i<n;i++) cout<<q[i]<<" ";
return 0;
}P3375 【模板】KMP
#include<iostream>
#include<cstring>
#define MAXN 1000010
using namespace std;
int kmp[MAXN];
int la,lb,j;
char a[MAXN],b[MAXN];
int main() {
cin>>a+1;
cin>>b+1;
la=strlen(a+1);
lb=strlen(b+1);
for (int i=2; i<=lb; i++) {
while(j&&b[i]!=b[j+1])
j=kmp[j];
if(b[j+1]==b[i])j++;
kmp[i]=j;
}
j=0;
for(int i=1; i<=la; i++) {
while(j>0&&b[j+1]!=a[i])
j=kmp[j];
if (b[j+1]==a[i])
j++;
if (j==lb) {
cout<<i-lb+1<<endl;
j=kmp[j];
}
}
for (int i=1; i<=lb; i++)
cout<<kmp[i]<<" ";
return 0;
}
搜索
二分图的判定:
int edge[maxn][maxn];//邻接矩阵存储
int color[maxn];//标记顶点颜色
int n,m;
bool dfs(int u,int c)
{
color[u]=c;//对u点进行染色
for(int i=1;i<=n;i++)//遍历与u点相连的点
{
if(edge[u][i]==1)//如果i点与u点相连
{
if(color[i]==c) return false;//i点的染色重复,则不是二分图
if(!color[i]&&!dfs(i,-c)) return false;//该点未染色,染上相反的颜色.dfs继续搜索
}
}
return true;//所有点染色完成之后,并且相邻顶点没有同色,则为二分图
}
P3386 【模板】二分图最大匹配
#include <bits/stdc++.h>
using namespace std; const int N=1005;
struct edge{
int to,nxt;
}e[200010];
int head[N],cnt,n,m,match[N],T;
bool vis[N];
void add(int x,int y){
e[++cnt]={y,head[x]};
head[x]=cnt;
}
bool dfs(int x){
for(int i=head[x];i;i=e[i].nxt){
int r=e[i].to;
if(vis[r]) continue;
vis[r]=true;
if(match[r]==0||dfs(match[r])){
match[r]=x;
return true;
}
}
return false;
}
int main(){
cin>>n>>m>>T;
while(T--){
int u,v;
cin>>u>>v;
add(u,v+n);
add(v+n,u);
}
int ans=0;
for(int i=1;i<=n;i++){
memset(vis,false,sizeof(vis));
if(dfs(i)) ans++;
}
cout<<ans<<endl;
return 0;
}
P7771 【模板】欧拉路径
#include <bits/stdc++.h>
using namespace std;
int n,m,u,v,d[100005],du[100005][2];
stack<int> st;
vector<int> G[100005];
void dfs(int now){
for(int i=d[now];i<G[now].size();i=d[now]){
d[now]=i+1;
dfs(G[now][i]);
}
st.push(now);
} int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>u>>v;
G[u].push_back(v);
du[u][1]++;
du[v][0]++;
}
for(int i=1;i<=n;i++){
sort(G[i].begin(),G[i].end());
}
int S=1,cnt[2]={0,0};
bool flag=1;
for(int i=1;i<=n;i++){
if(du[i][1]!=du[i][0]){
flag=0;
if(du[i][1]-du[i][0]==1) cnt[1]++,S=i;
else if(du[i][0]-du[i][1]==1) cnt[0]++;
else return puts("No"),0;
}
}
if(!flag&&!(cnt[0]==cnt[1]&&cnt[0]==1)) return puts("No"),0;
dfs(S);
while(!st.empty()) printf("%d ",st.top()),st.pop();
return 0;
}P8436【模板】边双连通分量
#include <iostream>
#include <cstring>
#include <vector> using namespace std; const int N = 500010, M = 2000010 * 2; int n, m;
int h[N], e[M], ne[M], idx;
int dfn[N], low[N], timestamp;
int stk[N], top;
int id[N], dcc_cnt;
bool is_bridge[M];
int tot[N];
vector <int> ans[N]; void add(int a, int b)
{
e[idx] = b;
ne[idx] = h[a];
h[a] = idx ++ ;
return;
} void tarjan(int u, int from)
{
dfn[u] = low[u] = ++ timestamp;
stk[ ++ top] = u; for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (!dfn[j])
{
tarjan(j, i);
low[u] = min(low[u], low[j]);
if (low[j] > dfn[u])
is_bridge[i] = is_bridge[i ^ 1] = true;
}
else if (i != (1 ^ from))
low[u] = min(low[u], dfn[j]);
} if (dfn[u] == low[u])
{
dcc_cnt ++ ;
int y;
do
{
y = stk[top -- ];
id[y] = dcc_cnt;
} while (y != u);
} return;
} int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> m;
memset(h, -1, sizeof h);
for (int i = 1; i <= m; i ++ )
{
int a, b;
cin >> a >> b;
add(a, b);
add(b, a);
} for (int i = 1; i <= n; i ++ )
if (!dfn[i]) tarjan(i, -1); cout << dcc_cnt << endl; for (int i = 1; i <= n; i ++ )
{
tot[id[i]] ++ ;
ans[id[i]].push_back(i);
} int p = 1;
while (tot[p])
{
cout << tot[p] << ' ';
for (int i = 0; i < ans[p].size(); i ++ )
cout << ans[p][i] << ' ';
p ++ ;
cout << endl;
} return 0;
}
P8435【模板】点双连通分量
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5, M = 4e6 + 5;
int cnt = 1, fir[N], nxt[M], to[M];
int s[M], top, bcc, low[N], dfn[N], idx, n, m;
vector<int> ans[N];
inline void tarjan(int u, int fa) {
int son = 0;
low[u] = dfn[u] = ++idx;
s[++top] = u;
for(int i = fir[u]; i; i = nxt[i]) {
int v = to[i];
if(!dfn[v]) {
son++;
tarjan(v, u);
low[u] = min(low[u], low[v]);
if(low[v] >= dfn[u]) {
bcc++;
while(s[top + 1] != v) ans[bcc].push_back(s[top--]);//将子树出栈
ans[bcc].push_back(u);//把割点/树根也丢到点双里
}
} else if(v != fa) low[u] = min(low[u], dfn[v]);
}
if(fa == 0 && son == 0) ans[++bcc].push_back(u);//特判独立点
}
inline void add(int u, int v) {
to[++cnt] = v;
nxt[cnt] = fir[u];
fir[u] = cnt;
}
int main() {
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; i++) {
int u, v;
scanf("%d%d", &u, &v);
add(u, v), add(v, u);
}
for(int i = 1; i <= n; i++) {
if(dfn[i]) continue;
top = 0;
tarjan(i, 0);
}
printf("%d\n", bcc);
for(int i = 1; i <= bcc; i++) {
printf("%d ", ans[i].size());
for(int j : ans[i]) printf("%d ", j);
printf("\n");
}
return 0;
}
DAG拓扑排序:
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 100;
const int M = 4e5 + 100;
int head[N], Next[M], ver[M], tot;
int deg[N];
void add(int x, int y) {
ver[++tot] = y;
Next[tot] = head[x];
head[x] = tot;
}
queue<int>qc;
int topsort(int n) {
int cnt = 0;
while (qc.size())
qc.pop();
for (int i = 1; i <= n; i++) {
if (deg[i] == 0) {
qc.push(i);
}
}
while (qc.size()) {
int x = qc.front();
cnt++;
qc.pop();
for (int i = head[x]; i; i = Next[i]) {
int y = ver[i];
if (--deg[y] == 0)
qc.push(y);
}
}
return cnt == n;
}
int main() {
int n, m, x, y;
cin >> n >> m;
for (int i = 1; i <= m; i++) {
cin >> x >> y;
add(x, y);
deg[y]++;
}
if (topsort(n))
cout << "Yes" << endl;
else
cout << "No" << endl;
}
Dijkstra \(O((n+m)\times log(n+m))\)
#include <bits/stdc++.h>
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl
#define FOR(i,a,b) for(int i=(a);i<=(b);++i)
#define ROF(i,a,b) for(int i=(a);i>=(b);--i)
#define U unsigned
#define LL long long
using namespace std;
template<class T>
inline void read(T &a){ register U LL 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); ch=getchar(); } a=x*t;}
inline void print(LL x){if(x<0)putchar('-'),x=-x;if(x>9) print(x/10);putchar(x%10+'0');}
int n,m,s;
vector<pair<int,int> > v[200005];
int dis[200005];
struct node{
int d,id;
bool friend operator < (node xx,node yy){
return xx.d>yy.d;
}
};
priority_queue<node>Q;
void dij(){
for(int i=1;i<=n;i++) dis[i]=2e9;
dis[s]=0;
Q.push({0,s});
while(!Q.empty()){
int d=Q.top().d;
int id=Q.top().id;
Q.pop();
if(d>dis[id]) continue;
for(int i=0;i<v[id].size();i++){
int to=v[id][i].first;
if(dis[to]>dis[id]+v[id][i].second){
dis[to]=dis[id]+v[id][i].second;
Q.push({dis[to],to});
}
}
}
}
void sovle(){
cin>>n>>m>>s;
for(int i=1;i<=m;i++){
int x,y,c;
cin>>x>>y>>c;
v[x].push_back({y,c});
}
dij();
for(int i=1;i<=n;i++){
cout<<dis[i]<<" ";
}
}
signed main(){
sovle();
return 0;
}
SPFA:
#include<bits/stdc++.h>
#define inf 1000000000
#define N 200020
using namespace std;
queue<int>Q;
int dis[N];
bool vis[N];
int n, m, s;
int head[N], pos;
struct edge { int to, next, c; }e[N];
void add(int a, int b, int c) {
pos++; e[pos].to = b, e[pos].c = c, e[pos].next = head[a], head[a] = pos;
}
void spfa() {
for (int i = 1; i <= n; i++)
dis[i] = inf, vis[i] = 0;
dis[s] = 0, vis[s] = 1, Q.push(s);
while (!Q.empty())
{
int u = Q.front(); Q.pop(); vis[u] = 0;
for (int i = head[u]; i; i = e[i].next)
{
int v = e[i].to;
if (dis[v] > dis[u] + e[i].c) {
dis[v] = dis[u] + e[i].c;
if (!vis[v])vis[v] = 1, Q.push(v);
}
}
}
}
int main()
{
cin >> n >> m >> s;
for (int i = 1; i <= m; i++) {
int x, y, c;
cin >> x >> y >> c;
add(x, y, c);
add(y, x, c);
}
spfa();
}
Floyd:
for (k = 1; k <= n; k++)
for (x = 1; x <= n; x++)
for (y = 1; y <= n; y++)
f[x][y] = min(f[x][y], f[x][k] + f[k][y]);
Bellman-Ford:
int dis[maxv][maxv]; //dis[k][v];表示选取前k个时到达i的最短距离
struct Edge
{
int u, v, w;
}edge[maxv];
int n, m; void Bellman_Ford(int s)
{
memset(dis, INF, sizeof(dis));
for (int i = 1; i <= n; i++) dis[i][s] = 0;
for (int k = 1; k <= n - 1; k++)
for (int i = 0; i < m; i++)
{
int u = edge[i].u, v = edge[i].v, w = edge[i].w;
dis[k][v] = min(dis[k][v], dis[k - 1][u] + w);
}
}
Kosaraju:
#include<bits/stdc++.h>
#define N 200020
using namespace std;
int n, m;
int head[N], pos;
struct edge { to, next, c; }e[N << 1];
void add(int a, int b, int c)
{
pos++; e[pos].to = b, e[pos].next = head[a], head[a] = pos; e[pos].c = c;
}
int tot, scc;
int st[N], top;
bool vis[N];
void dfs1(int u)
{
vis[u] = 1;
for (int i = head[u]; i; i = e[i].next)
{
int v = e[i].to;
if (vis[v] || e[i].c == 0)continue;
dfs1(v);
}
st[++top] = u;
}
int bel[N];
void dfs2(int u, int bl)
{
bel[u] = bl, vis[u] = 1;
for (int i = head[u]; i; i = e[i].next)
{
int v = e[i].to;
if (vis[v] || e[i].c)continue;
dfs2(v, bl);
}
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= m; i++)
{
int x, y;
cin >> x >> y;
add(x, y, 1), add(y, x, 0);
}
for (int i = 1; i <= n; i++)
if (!vis[i])dfs1(i);
for (int i = 1; i <= n; i++)
vis[i] = 0;
for (int i = top; i; i--)
if (!vis[st[i]]) {
++scc;
dfs2(st[i], scc);
}
cout << scc << endl; //scc是强连通分量的数量
}
Tarjan(强连通分量):
#include<bits/stdc++.h>
using namespace std; int n,m,cnt,cntb;
vector<int> edge[10001];
vector<int> belong[10001];
bool instack[10001];
int dfn[10001];
int low[10001];
stack<int> s;
void Tarjan(int u){
++cnt;
dfn[u]=low[u]=cnt;
s.push(u);
instack[u]=true;
for(int i=0;i<edge[u].size();++i){
int v=edge[u][i];
if(!dfn[v]){
Tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(instack[v])
low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]){
++cntb;
int node;
do{
node=s.top();
s.pop();
instack[node]=false;
belong[cntb].push_back(node);
}while(node!=u);
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=m;++i){
int u,v;
cin>>u>>v;
edge[u].push_back(v);
}
Tarjan(1);
return 0;
}
Tarjan:割点
#include <bits/stdc++.h>
using namespace std;
constexpr int N = 1e5 + 5;
int n, m, num,root;
int dn, dfn[N], low[N], cnt, buc[N],sum;
vector<int> e[N];
void Tarjan(int x){
dfn[x]=low[x]=++num;
int flag=0;
for(int i=0;i<e[x].size();i++){
int to=e[x][i];
if(!dfn[to]){
Tarjan(to);
low[x]=min(low[x],low[to]);
if(low[to]>=dfn[x]){
flag++;
if(x!=root||(flag>=2)){
buc[x]=1;
}
}
}
else low[x]=min(low[x],dfn[to]);
}
}
int main() {
cin >> n >> m;
for(int i = 1; i <= m; i++) {
int u, v;
cin >> u >> v;
e[u].push_back(v);
e[v].push_back(u);
}
for(int i=1;i<=n;i++){
if(!dfn[i]){
root=i;
Tarjan(i);
}
}
for(int i=1;i<=n;i++){
if(buc[i]) sum++;
}
cout<<sum<<endl;
for(int i=1;i<=n;i++){
if(buc[i]) cout<<i<<" ";
}
return 0;
}
Tarjan: 割边
#include<iostream>
#include<cmath>
using namespace std;
int n, m, root, a, b, total;
int e[101][101], dfn[101], low[101], flag[101], head[101]; struct node{
int to;
int next;
}edge[10010]; int cnt = 1;
void add(int u, int v) {
edge[cnt].to = v;
edge[cnt].next = head[u];
head[u] = cnt++;
} void tarjan(int u, int father) {
int child = 0;
dfn[u] = low[u] = ++total;
for (int i = head[u]; i != 0; i = edge[i].next) {
int v = edge[i].to;
if (!dfn[v]) {
child++;
tarjan(v, u);
low[u] = min(low[u], low[v]);
if (low[v] > dfn[u]) {
cout << u << "->" << v << endl;
}
} else if (v != father) {
low[u] = min(low[u], dfn[v]);
}
}
} int main() {
cin >> n >> m;
for (int i = 0; i < m; i++) {
cin >> a >> b;
add(a, b);
add(b, a);
}
root = 1;
tarjan(1, root);
for (int i = 1; i <= n; i++) {
if(flag[i]) {
cout << i << " ";
}
}
return 0;
}
差分约束:
#include <cstring>
#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;
struct edge {
int v,w,next;
} e[10005];
int head[5005],tot[5005],dis[5005],vis[5005],cnt,n,m;
void addedge(int u,int v,int w) {
e[++cnt].v=v;
e[cnt].w=w;
e[cnt].next=head[u];
head[u]=cnt;
}
bool spfa(int s) {
queue<int> q;
memset(dis,63,sizeof(dis));
dis[s]=0,vis[s]=1;
q.push(s);
while(!q.empty()) {
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u]; i; i=e[i].next) {
int v=e[i].v;
if(dis[v]>dis[u]+e[i].w) {
dis[v]=dis[u]+e[i].w;
if(!vis[v]) {
vis[v]=1,tot[v]++;
if(tot[v]==n+1)return false; // 注意添加了一个超级源点
q.push(v);
}
}
}
}
return true;
}
int main() {
cin>>n>>m;
for(int i=1; i<=n; i++)
addedge(0,i,0);
for(int i=1; i<=m; i++) {
int v,u,w;
cin>>v>>u>>w;
addedge(u,v,w);
}
if(!spfa(0))cout<<"NO"<<endl;
else
for(int i=1; i<=n; i++)
cout<<dis[i]<<' ';
return 0;
}
严格单元次短路:
#include <bits/stdc++.h>
using namespace std; const int MAXN = 200010;
struct edge{
int to,nxt,v;
}e[MAXN];
int h[MAXN],cnt,dis[2][MAXN],n,m;
void add(int u,int v,int w){
e[++cnt].nxt=h[u];
e[cnt].to=v;
e[cnt].v=w;
h[u]=cnt;
}
struct node{
int pos,dis;
friend bool operator<(node a,node b){
return a.dis>b.dis;
}
}tmp; priority_queue<node> q;
void dij(){
for(int i=1;i<=n;i++){
dis[0][i]=dis[1][i]=2147483647;
}
dis[0][1]=0;
tmp.dis=0,tmp.pos=1;
q.push(tmp);
while(!q.empty()){
tmp=q.top();
q.pop();
int u=tmp.pos,d=tmp.dis;
if(d>dis[1][u]) continue;
for(int i=h[u];i;i=e[i].nxt){
int v=e[i].to;
int w=e[i].v;
if(dis[0][v]>d+w){
dis[1][v]=dis[0][v];
tmp.dis=dis[0][v]=d+w;
tmp.pos=v;
q.push(tmp);
}
if(dis[1][v]>d+w&&dis[0][v]<d+w){
tmp.dis=dis[1][v]=d+w;
tmp.pos=v;
q.push(tmp);
}
}
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
add(b,a,c);
} dij();
cout<<dis[1][n];
}
严格次小生成树:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 100010;
const ll INF = 1e18;
int n, m,d[N];
bool vis[N];
ll f[N][25],g1[N][25],g2[N][25],mst, ans = INF;
struct Node {
ll to,cost;
};
vector<Node> v[N]; void dfs(const int x) {
vis[x] = true;
for (int i = 0; i < v[x].size(); i++) {
int y = v[x][i].to;
if (vis[y]) continue;
d[y] = d[x] + 1;
f[y][0] = x;
g1[y][0] = v[x][i].cost;
g2[y][0] = -INF;
dfs(y);
}
} inline void prework() {
for (int i = 1; i <= 20; i++)
for (int j = 1; j <= n; j++) {
f[j][i] = f[f[j][i - 1]][i - 1];
g1[j][i] = max(g1[j][i - 1], g1[f[j][i - 1]][i - 1]);
g2[j][i] = max(g2[j][i - 1], g2[f[j][i - 1]][i - 1]);
if (g1[j][i - 1] > g1[f[j][i - 1]][i - 1]) g2[j][i] = max(g2[j][i], g1[f[j][i - 1]][i - 1]);
else if (g1[j][i - 1] < g1[f[j][i - 1]][i - 1]) g2[j][i] = max(g2[j][i], g1[j][i - 1]);
}
} inline void LCA(int x, int y, const ll w) {
ll zui = -INF, ci = -INF;
if (d[x] > d[y]) swap(x, y);
for (int i = 20; i >= 0; i--)
if (d[f[y][i]] >= d[x]) {
zui = max(zui, g1[y][i]);
ci = max(ci, g2[y][i]);
y = f[y][i];
}
if (x == y) {
if (zui != w) ans = min(ans, mst - zui + w);
else if (ci != w && ci > 0) ans = min(ans, mst - ci + w);
return;
}
for (int i = 20; i >= 0; i--)
if (f[x][i] != f[y][i]) {
zui = max(zui, max(g1[x][i], g1[y][i]));
ci = max(ci, max(g2[x][i], g2[y][i]));
x = f[x][i];
y = f[y][i];
}
zui = max(zui, max(g1[x][0], g1[y][0]));
if (g1[x][0] != zui) ci = max(ci, g1[x][0]);
if (g2[y][0] != zui) ci = max(ci, g2[y][0]);
if (zui != w) ans = min(ans, mst - zui + w);
else if (ci != w && ci > 0) ans = min(ans, mst - ci + w);
} struct Edge {
int from, to;
ll cost;
bool is_tree;
} edge[N * 3]; bool operator < (const Edge x, const Edge y) {
return x.cost < y.cost;
} int fa[N]; inline int find(const int x) {
if (fa[x] == x) return x;
else return fa[x] = find(fa[x]);
} inline void Kruskal() {
sort(edge, edge + m);
for (int i = 1; i <= n; i++)
fa[i] = i;
for (int i = 0; i < m; i++) {
int x = edge[i].from;
int y = edge[i].to;
ll z = edge[i].cost;
int a = find(x), b = find(y);
if (a == b) continue;
fa[find(x)] = y;
mst += z;
edge[i].is_tree = true;
v[x].push_back((Node) {
y, z
});
v[y].push_back((Node) {
x, z
});
}
} int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cin >> n >> m;
for (int i = 0, x, y; i < m; i++) {
ll z;
cin >> x >> y >> z;
if (x == y) continue;
edge[i].from = x;
edge[i].to = y;
edge[i].cost = z;
}
Kruskal();
d[1] = 1;
dfs(1);
prework();
for (int i = 0; i < m; i++)
if (!edge[i].is_tree)
LCA(edge[i].from, edge[i].to, edge[i].cost);
cout << ans << "\n";
return 0;
}
Kruskal:
#include <bits/stdc++.h>
using namespace std;
const int N=2000200;
int n,m,f[N];
long long ans=0;
struct edge{
int u,v,c;
}e[N];
bool operator<(edge x,edge y){
return x.c<y.c;
}
int find(int x){
return x==f[x]?x:f[x]=find(f[x]);
}
void sovle(){
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>e[i].u>>e[i].v>>e[i].c;
}
sort(e+1,e+m+1);
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=m;i++){
int fu=find(e[i].u),fv=find(e[i].v);
if(fu==fv) continue;
f[fu]=fv;
ans+=e[i].c;
}
cout<<ans<<endl;
} signed main(){
sovle();
return 0;
}
Prim:
#include <bits/stdc++.h>
using namespace std; const int maxn = 5010;
const int inf = 0x3f3f3f3f;
int g[maxn][maxn];
int dis[maxn];
bool vis[maxn];
int n, m, u, v, w,cnt; int prime() {
int tot = 0;
memset(dis, inf, sizeof(dis));
memset(vis, false, sizeof(vis));
dis[1] = 0;
for (int i = 1; i <= n; ++i) {
int minv = inf, u = -1;
for (int j = 1; j <= n; ++j) {
if (!vis[j] && minv > dis[j]) {
minv = dis[j];
u = j;
}
}
if (u == -1) return -1;
vis[u] = true;
tot += minv;
cnt++;
for (int j = 1; j <= n; ++j) {
if (!vis[j] && dis[j] > g[u][j]) {
dis[j] = g[u][j];
}
}
}
return tot;
} int main() {
memset(g, inf, sizeof(g));
cin >> n >> m;
for (int i = 0; i < m; ++i) {
cin >> u >> v >> w;
g[u][v] = g[v][u] = min(g[u][v], w);
}
int tmp=prime();
if(cnt>=n-1)
cout << tmp << endl;
return 0;
}
树的直径:
#include <bits/stdc++.h>
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl
using namespace std;
int dp[100010]={0},ans;
vector<int> G[100010];
void dfs(int rt,int fa){
dp[rt]=0;
int mx=0,mn=0;
for(int i=0;i<G[rt].size();i++){
int to=G[rt][i];
if(to==fa) continue;
dfs(to,rt);
if(dp[to]>mx){
mn=mx;
mx=dp[to];
}
else if(dp[to]>mn) mn=dp[to];
}
dp[rt]=mx+1;
ans=max(ans,mx+mn); }
int main(){
int n;
cin>>n;
for(int i=1;i<=n-1;i++){
int u,v;
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1,-1);
cout<<ans<<endl;
return 0; }
树的重心:
#include <bits/stdc++.h>
using namespace std; const int MAXN=1000010;
const int inf=0x7f7f7f7f; int f[MAXN],size[MAXN],head[MAXN],dep[MAXN];
int n,center,sum;
vector<int> G[MAXN];
queue<int> q; void dfs(int u,int fa){
size[u]=1;
f[u]=0;
for(int i=0;i<G[u].size();i++){
int v=G[u][i];
if(v==fa) continue;
dfs(v,u);
size[u]+=size[v];
f[u]=max(f[u],size[v]);
}
f[u]=max(f[u],n-size[u]);
if(f[u]<f[center]||(f[u]==f[center]&&u<center)){
center=u;
}
} void bfs(){
q.push(center);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=0;i<G[u].size();i++){
int v=G[u][i];
if(dep[v]||v==center) continue;
dep[v]=dep[u]+1;
sum+=dep[v];
q.push(v);
}
}
}
int main(){
cin>>n;
for(int i=1;i<=n-1;i++){
int u,v;
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
center=0;
f[0]=inf;
dfs(1,0);
bfs();
cout<<center<<" "<<sum<<endl;
return 0;
}
树的中心:
#include<bits/stdc++.h>
using namespace std;
const int N = 10010, M = N * 2, INF = 0x3f3f3f3f;
int n;
int h[N], e[M], w[M], ne[M], idx;
int d1[N], d2[N], p1[N], up[N];
bool is_leaf[N];
void add(int a, int b, int c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
int dfs_d(int u, int father) {
d1[u] = d2[u] = -INF;
for (int i = h[u]; i != -1; i = ne[i]) {
int j = e[i];
if (j == father) continue;
int d = dfs_d(j, u) + w[i];
if (d >= d1[u]) {
d2[u] = d1[u], d1[u] = d;
p1[u] = j;
} else if (d > d2[u]) d2[u] = d;
}
if (d1[u] == -INF) {
d1[u] = d2[u] = 0;
is_leaf[u] = true;
}
return d1[u];
}
void dfs_u(int u, int father) {
for (int i = h[u]; i != -1; i = ne[i]) {
int j = e[i];
if (j == father) continue; if (p1[u] == j) up[j] = max(up[u], d2[u]) + w[i];
else up[j] = max(up[u], d1[u]) + w[i]; dfs_u(j, u);
}
}
int main() {
cin >> n;
memset(h, -1, sizeof h);
for (int i = 0; i < n - 1; i ++ ) {
int a, b, c;
cin >> a >> b >> c;
add(a, b, c), add(b, a, c);
}
dfs_d(1, -1);
dfs_u(1, -1);
int res = d1[1];
for (int i = 2; i <= n; i ++ )
if (is_leaf[i]) res = min(res, up[i]);
else res = min(res, max(d1[i], up[i]));
printf("%d\n", res);
return 0;
}
LCA:
#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
using namespace std;
typedef pair<int,int> PII;
const int inf = 0x3f3f3f3f;
const int mod = 998244353;
const int N = 5e5+10;
const int base = 233;
int n,m,s;
int pre[N][35],dep[N];
vector<int>v[N];
void init(int l,int ff){
pre[l][0] = ff;
dep[l] = dep[ff] + 1;
for(int r:v[l]){
if(r==ff)continue;
init(r,l);
}
}
void init(){
init(s,0);
for(int k=1;k<=25;k++){
for(int i=1;i<=n;i++){
pre[i][k] = pre[ pre[i][k-1] ][k-1];
}
}
}
int LCA(int x,int y){
//1.同深度
if(dep[x]<dep[y])swap(x,y);
for(int k=25;k>=0;k--){
int fa = pre[x][k];
if(dep[fa]>=dep[y])x = fa;
}
if(x==y)return x;
//2.一起向上跳
for(int k=25;k>=0;k--){
int fax = pre[x][k];
int fay = pre[y][k];
if(fax != fay){
x = fax;
y = fay;
}
}
return pre[y][0];
}
inline void slove(){
scanf("%d%d%d",&n,&m,&s);
for(int i=1;i<n;i++){
int x,y;scanf("%d%d",&x,&y);
v[x].push_back(y);
v[y].push_back(x);
}
init();
while (m--){
int x,y;scanf("%d%d",&x,&y);
printf("%d\n",LCA(x,y));
}
}
int main(){
int TT=1;
while(TT--) slove();
return 0;
}
树上差分
#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
using namespace std;
typedef pair<int,int> PII;
const int inf = 0x3f3f3f3f;
const int mod = 998244353;
const int N = 5e5+10;
const int base = 233;
int n,m,s;
int pre[N][35],dep[N],val[N];
vector<int>v[N];
void init(int l,int ff){
dep[l] = dep[ff] + 1;
pre[l][0] = ff;
for(int r:v[l]){
if(r==ff)continue;
init(r,l);
}
}
void init(){
init(1,0);
for(int k=1;k<=25;k++){
for(int i=1;i<=n;i++){
pre[i][k] = pre[pre[i][k-1]][k-1];
}
}
}
int lca(int x,int y){
if(dep[x]<dep[y])swap(x,y);
for(int k=25;k>=0;k--){
if(dep[pre[x][k]]>=dep[y]) x = pre[x][k];
}
if(x==y)return x;
for(int k=25;k>=0;k--){
if(pre[x][k] != pre[y][k])x = pre[x][k],y = pre[y][k];
}
return pre[x][0];
}
int ans = 0;
void dfs(int l,int ff){
for(int r:v[l]){
if(r==ff)continue;
dfs(r,l);
val[l] += val[r];
}
ans = max(ans,val[l]);
}
inline void slove(){
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++){
int x,y;scanf("%d%d",&x,&y);
v[x].push_back(y);
v[y].push_back(x);
}
init();
while (m--){
int x,y;scanf("%d%d",&x,&y);
val[x]++;
val[y]++;
int LCA = lca(x,y);
val[LCA]--;
val[pre[LCA][0]]--;
}
dfs(1,0);
printf("%d\n",ans);
}
int main(){
int TT=1;
while(TT--)slove();
return 0;
}最长上升子序列:
\(O(n^2)\)
#include <bits/stdc++.h>
using namespace std;
int main(){
int n,a[100005],dp[100005],MAX;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++){
MAX=0;
for(int j=1;j<=i-1;j++)
if(a[i]>a[j]) MAX=max(MAX,dp[j]);
dp[i]=MAX+1;
}
MAX=0;
for(int i=1;i<=n;i++) MAX=max(MAX,dp[i]);
cout<<MAX<<endl;
}
\(O(nlogn)\)
#include <bits/stdc++.h>
using namespace std;
int a[10005],f[10005];
int cnt;
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++){
int pos=lower_bound(f+1,f+cnt+1,a[i])-f;
if(pos==cnt+1) f[++cnt]=a[i];
else f[pos]=a[i];
}
cout<<cnt<<endl;
for(int i=1;i<=cnt;i++) cout<<f[i]<<" ";
return 0;
}最长公共子序列:
\(O(n^2)\)
#include<iostream>
using namespace std;
int dp[1001][1001],a1[2001],a2[2001],n,m;
int main(){
cin>>n;
for(int i=1;i<=n;i++)scanf("%d",&a1[i]);
for(int i=1;i<=n;i++)scanf("%d",&a2[i]);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
if(a1[i]==a2[j])
dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1);
}
cout<<dp[n][n];
}
\(O(nlogn)\)
#include<bits/stdc++.h>
using namespace std;
int a[100001],b[100001],mp[100001],f[100001];
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
mp[a[i]]=i;
}
for(int i=1;i<=n;i++){
cin>>b[i];
f[i]=0x7fffffff;
}
int len=0;
f[0]=0;
for(int i=1;i<=n;i++){
int l=0,r=len,mid;
if(mp[b[i]]>f[len])f[++len]=mp[b[i]];
else
{
int pos=lower_bound(f,f+len,mp[b[i]])-f;
f[pos]=min(mp[b[i]],f[pos]);
}
}
cout<<len;
return 0;
}01背包:
空间复杂度 \(O(nV)\)
#include <bits/stdc++.h>
using namespace std;
int f[1005][1005]; //f[i][j]表示选前i个物品重量不超过j的最大值
int main(){
int n,V;
cin>>n>>V;
for(int i=1;i<=n;i++){
int v,w;
cin>>v>>w;
for(int j=1;j<=V;j++){
if(v<=j)
f[i][j]=max(f[i-1][j],f[i-1][j-v]+w);
else
f[i][j]=f[i-1][j];
}
}
cout<<f[n][V]<<endl;
return 0;
}
空间复杂度 \(O(V)\)
#include <bits/stdc++.h>
using namespace std;
int f[1005];
int main(){
int n,V;
cin>>n>>V;
for(int i=1;i<=n;i++){
int v,w;
cin>>v>>w;
for(int j=V;j>=v;j--) f[j]=max(f[j],f[j-v]+w);
}
cout<<f[V]<<endl;
return 0;
}
二维费用01背包
#include <iostream>
using namespace std;
const int N = 110;
int n, V, M,f[N][N];
int main(){
cin >> n >> V >> M;
for (int i = 0; i < n; i ++ ){
int v, m, w;
cin >> v >> m >> w;
for (int j = V; j >= v; j -- )
for (int k = M; k >= m; k -- )
f[j][k] = max(f[j][k], f[j - v][k - m] + w);
}
cout << f[V][M] << endl;
return 0;
}
01背包求具体方案:
#include <iostream>
using namespace std;
const int N = 1010;
int n, m,v[N], w[N],f[N][N];
int main(){
cin >> n >> m;
for (int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i];
for (int i = n; i >= 1; i -- ) //要求输出字典序最小所以考虑倒着做,即 f[1][m] 为最大价值
for (int j = 0; j <= m; j ++ ){
f[i][j] = f[i + 1][j];
if (j >= v[i]) f[i][j] = max(f[i][j], f[i + 1][j - v[i]] + w[i]);
}
int j = m;
for (int i = 1; i <= n; i ++ )
if (j >= v[i] && f[i][j] == f[i + 1][j - v[i]] + w[i]){
cout << i << ' ';
j -= v[i];
}
return 0;
}
完全背包:
空间复杂度 \(O(nV)\)
#include <bits/stdc++.h>
using namespace std;
int f[1005][1005];
int main(){
int n,V;
cin>>n>>V;
for(int i=1;i<=n;i++){
int v,w;
cin>>v>>w;
for(int j=0;j<=V;j++){
f[i][j]=f[i-1][j];
if(v<=j)
f[i][j]=max(f[i][j],f[i][j-v]+w);
}
}
cout<<f[n][V]<<endl;
return 0;
}
空间复杂度 \(O(V)\)
#include <bits/stdc++.h>
using namespace std;
int f[1005];
int main(){
int n,V;
cin>>n>>V;
for(int i=1;i<=n;i++){
int v,w;
cin>>v>>w;
for(int j=v;j<=V;j++) f[j]=max(f[j],f[j-v]+w);
}
cout<<f[V]<<endl;
return 0;
}
多重背包:
时间复杂度 \(O(nVs)\) (朴素版)
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
int v[N], w[N], s[N],f[N][N],n, m;
int main(){
cin >> n >> m;
for(int i = 1; i <= n; i ++) cin >> v[i] >> w[i] >> s[i];
for(int i = 1; i <= n; i ++){//枚举背包
for(int j = 1; j <= m; j ++){//枚举体积
for(int k = 0; k <= s[i]; k ++)
if(j >= k * v[i])
f[i][j] = max(f[i][j], f[i - 1][j - k * v[i]] + k * w[i]);
}
}
cout << f[n][m] << endl;
return 0;
}
时间复杂度 \(O(n \ logs V)\) (二进制分解)
#include<bits/stdc++.h>
using namespace std;
const int N = 12010, M = 2010;
int n, m,v[N], w[N],f[M];
int main(){
cin >> n >> m;
int cnt = 0; //分组的组别
for(int i = 1;i <= n;i ++){
int a,b,s;
cin >> a >> b >> s;
int k = 1; // 组别里面的个数
while(k<=s){
cnt ++ ; //组别先增加
v[cnt] = a * k ; //整体体积
w[cnt] = b * k; // 整体价值
s -= k; // s要减小
k *= 2; // 组别里的个数增加
}
//剩余的一组
if(s>0){
cnt ++ ;
v[cnt] = a*s;
w[cnt] = b*s;
}
}
n = cnt ; //枚举次数正式由个数变成组别数
//01背包一维优化
for(int i = 1;i <= n ;i ++)
for(int j = m ;j >= v[i];j --)
f[j] = max(f[j],f[j-v[i]] + w[i]);
cout << f[m] << endl;
return 0;
}
时间复杂度 \(O(nV)\) (单调队列优化)
#include<bits/stdc++.h>
using namespace std;
const int N = 20010;
int n, m,f[N], g[N], q[N];
int main(){
cin >> n >> m;
for (int i = 0; i < n; i ++ ){
int v, w, s;
cin >> v >> w >> s;
memcpy(g, f, sizeof f); //滚动数组
for (int j = 0; j < v; j ++ ){
int hh = 0, tt = -1;
for (int k = j; k <= m; k += v){ //单调队列求区间最大值
if (hh <= tt && q[hh] < k - s * v) hh ++ ;
while (hh <= tt && g[q[tt]] - (q[tt] - j) / v * w <= g[k] - (k - j) / v * w) tt -- ;
q[ ++ tt] = k;
f[k] = g[q[hh]] + (k - q[hh]) / v * w;
}
}
}
cout << f[m] << endl;
return 0;
}分组背包:
\(O(nms)\)
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int f[N],v[N][N],w[N][N],s[N],n,m,k;
int main(){
cin>>n>>m;
for(int i=0;i<n;i++){
cin>>s[i];
for(int j=0;j<s[i];j++)
cin>>v[i][j]>>w[i][j];
}
for(int i=0;i<n;i++){ //枚举组
for(int j=m;j>=0;j--){
for(int k=0;k<s[i];k++){ //for(int k=s[i];k>=0;k--)也可以
if(j>=v[i][k]) f[j]=max(f[j],f[j-v[i][k]]+w[i][k]);
}
}
}
cout<<f[m]<<endl;
}
分组背包求具体方案
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int f[N][N],g[N][N],res[N]; //g[i][j] 为第i组的体积为j的价值
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>g[i][j];
for(int i=n;i;i--) //要求输出字典序最小所以考虑倒着做,即 f[1][m] 为最大价值
for(int j=0;j<=m;j++)
for(int k=0;k<=j;k++)
f[i][j]=max(f[i][j],f[i+1][j-k]+g[i][k]);
cout<<f[1][m]<<endl;
int j=m;
for(int i=1;i<=n;i++)
for(int k=0;k<=j;k++)
if(f[i][j]==f[i+1][j-k]+g[i][k]){
res[i]=k;
j-=k;
break;
}
for(int i=1;i<=n;i++)
cout<<i<<" "<<res[i]<<endl;
return 0;
}
其他dp
2.2.5 数学与其他
康托展开:
#include<cstdio>
using namespace std;
#define N 1000001
int n,tr[N];
long long ans,fac[N];
void add(int x,int k) {
for (; x<=n; x+=x&-x) tr[x]+=k;
}
int query(int x) {
int t=0;
for (; x; x-=x&-x) t+=tr[x];
return t;
}
int main() {
scanf("%d",&n);
fac[0]=1;
for (int i=1; i<=n; i++) {
fac[i]=fac[i-1]*i%998244353;
add(i,1);
}
for (int i=1,x; i<=n; i++) {
scanf("%d",&x);
ans=(ans+(query(x)-1)*fac[n-i])%998244353;
add(x,-1);
}
printf("%lld",ans+1);
return 0;
}
中国剩余定理:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 10;
int n;
int A[N], B[N];
LL exgcd(LL a, LL b, LL &x, LL &y) // 扩展欧几里得算法, 求x, y,使得ax + by = gcd(a, b){
if (!b){
x = 1; y = 0;
return a;
}
LL d = exgcd(b, a % b, y, x);
y -= (a / b) * x;
return d;
}
int main(){
scanf("%d", &n);
LL M = 1;
for (int i = 0; i < n; i ++ ){
scanf("%d%d", &A[i], &B[i]);
M *= A[i];
}
LL res = 0;
for (int i = 0; i < n; i ++ )
{
LL Mi = M / A[i];
LL ti, x;
exgcd(Mi, A[i], ti, x);
res = (res + (__int128)B[i] * Mi * ti) % M;
// B[i] * Mi * ti可能会超出long long范围,所以需要转化成__int128
}
cout << (res % M + M) % M << endl;
return 0;
}
线性筛:
void work(int n){
numlist[1]=1;
for(int i=2;i<=n;i++){
if(numlist[i]==false) prime[++cnt]=i;
for(int j=1; j<=cnt&&i*prime[j]<=n; j++){
numlist[i*prime[j]] = true ;
if(i%prime[j]==0) break;
}
}
}
欧拉筛
int st[N]; // 初始化为0, 0表示质数,1表示合数
for(int i = 2; i <= n; i++){
for(int j = 2; j <= i / j; j++){
if(i % j == 0){
st[i] = 1;
}
}
}
快速幂:
int qmi(int a, int k){
int res = 1;
while (k){
if (k & 1) res = (LL)res * a % mod;
a = a * a % mod;
k >>= 1;
}
return res;
}
欧拉函数:
void init(int n)
{
phi[1] = 1;
for (int i = 2; i <= n; i ++ )
{
if (!st[i])
{
primes[cnt ++ ] = i;
phi[i] = i - 1;
}
for (int j = 0; primes[j] * i <= n; j ++ )
{
st[i * primes[j]] = true;
if (i % primes[j] == 0)
{
phi[i * primes[j]] = phi[i] * primes[j];
break;
}
phi[i * primes[j]] = phi[i] * (primes[j] - 1);
}
}
}拓展欧几里得:
int exgcd(int a, int b, int &x, int &y)
{
if (!b)
{
x = 1, y = 0;
return a;
} int d = exgcd(b, a % b, y, x);
y -= a / b * x; return d;
}
逆元
费马小定理求逆元:
LL pow_mod(LL a, LL b, LL p){//a的b次方求余p
LL ret = 1;
while(b){
if(b & 1) ret = (ret * a) % p;
a = (a * a) % p;
b >>= 1;
}
return ret;
}
LL Fermat(LL a, LL p){//费马求a关于b的逆元
return pow_mod(a, p-2, p);
}
扩展欧基里德求逆元:
#include<cstdio>
typedef long long LL;
void ex_gcd(LL a, LL b, LL &x, LL &y, LL &d){
if (!b) {d = a, x = 1, y = 0;}
else{
ex_gcd(b, a % b, y, x, d);
y -= x * (a / b);
}
}
LL inv(LL t, LL p){//如果不存在,返回-1
LL d, x, y;
ex_gcd(t, p, x, y, d);
return d == 1 ? (x % p + p) % p : -1;
}
int main(){
LL a, p;
while(~scanf("%lld%lld", &a, &p)){
printf("%lld\n", inv(a, p));
}
}
线性求逆元:
Inv[ 1 ] = 1;
for( int i = 2; i <= n; i++ )
Inv[ i ] = ( p - p / i ) * Inv[ p % i ] % p;
组合数:
杨辉三角:
#include <iostream>
using namespace std;
typedef long long LL;
const int N = 2010, MOD = 1e9+7;
int n;
int c[N][N];
void init() {
for (int i = 0; i < N; ++i)
for (int j = 0; j <= i; ++j)
if (!j) c[i][j] = 1;
else c[i][j] = (c[i-1][j] + c[i-1][j-1]) % MOD;
}
int main() {
init();
int n;
cin >> n;
while (n --) {
int a, b;
cin >> a >> b;
cout << c[a][b] << endl;
}
return 0;
}
预处理阶乘+逆元:
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 1e5+5, MOD = 1e9+7;
int fact[N], infact[N];
int qmi(int a, int k, int p) {
LL res = 1;
while (k) {
if (k & 1) res = (LL)res * a % p;
k >>= 1;
a = (LL)a * a % p;
}
return res;
}
int main() {
fact[0] = infact[0] = 1;
for (int i = 1 ; i < N; ++i) {
fact[i] = (LL)fact[i - 1] * i % MOD;
infact[i] = (LL)infact[i - 1] * qmi(i, MOD - 2, MOD) % MOD;
} int n;
cin >> n;
while (n --) {
int a, b;
cin >> a >> b; cout << (LL)fact[a] * infact[a - b] % MOD * infact[b] % MOD << endl;
}
return 0;
}
Lucas定理
#include <iostream>
using namespace std;
typedef long long LL;
int qmi(int a, int k, int p) {
int res = 1 % p;
while (k) {
if (k & 1) res = (LL)res * a % p;
a = (LL)a * a % p;
k >>= 1;
}
return res;
}
// 定义求解
int C(int a, int b, int p) {
if (b > a) return 0;
int res = 1;
for (int i = 1, j = a; i <= b; ++i, --j) {
res = (LL)res * j % p;
res = (LL)res * qmi(i, p - 2, p) % p;
}
return res;
}
int lucas(LL a, LL b, LL p) { // 注意LL参数类型
if (a < p && b < p) return C(a, b, p);
return (LL)C(a % p, b % p, p) * lucas(a / p, b / p, p) % p; // 递归让其到p范围内求解
}
int main() {
int n;
cin >> n; while (n --) {
LL a, b, p;
cin >> a >> b >> p;
cout << (LL)lucas(a, b, p) << endl;
}
return 0;
}
高斯消元:
#include <iostream>
#include <cmath>
using namespace std;
const int N = 105;
const double eps = 1e-6;
int n;
double a[N][N];
int gauss() {
int c, r;
for (c = 0, r = 0; c < n; ++c) { // c列r行,遍历列
int t = r;
for (int i = r; i < n; ++i) // 寻找列主元,拿t记录
if (fabs(a[i][c]) > fabs(a[t][c]))
t = i;
if (fabs(a[t][c]) < eps) continue; // 如果列主元为0,不必考虑,当前列全为0 // 交换列主元t行与当前r行的数
for (int i = c; i < n + 1; ++i) swap(a[t][i], a[r][i]);
// 当前列主元已经被交换到了r行,需要从后向前进行处理,避免主对角线元素变成1
for (int i = n; i >= c; --i) a[r][i] /= a[r][c];
// 列主消元
for (int i = r + 1; i < n; ++i)
if (fabs(a[i][c]) > eps)
for (int j = n; j >= c; --j)
a[i][j] -= a[r][j] * a[i][c];
++r;
}
if (r < n) {
for (int i = r; i < n; ++i)
if (fabs(a[i][n]) > eps) return 2; // 0x=1 则无解 return 1; // 0x=0 无穷多解
}
// 上三角阶梯型矩阵
// 直接求解即可,最后一列放置结果
for (int i = n - 1; i >= 0; --i)
for (int j = i + 1; j < n; ++j)
a[i][n] -= a[j][n] * a[i][j];
return 0;
}
int main() {
cin >> n;
for (int i = 0; i < n; ++i)
for (int j = 0; j < n + 1; ++j)
cin >> a[i][j];
int t = gauss();
if (t == 0) {
for (int i = 0; i < n; ++i) printf("%.2lf\n", a[i][n]);
}
else if (t == 1) puts("Infinite group solutions");
else puts("No solution");
return 0;
}
龟速乘
LL qmul(LL a, LL k, LL p) {
LL res = 0;
while (k){
if (k & 1) res = (res + a) % p;
a = (a + a) % p;
k >>= 1;
}
return res;
}
莫比乌斯函数
mu[1]=1;
for(i=2;i<=n;i++){
if(!not_prime[i]){
prime[++tot]=i;
mu[i]=-1;
}
for(j=1;prime[j]*i<=n;j++){
not_prime[prime[j]*i]=1;
if(i%prime[j]==0){
mu[prime[j]*i]=0;
break;
}
mu[prime[j]*i]=-mu[i];
}
}
CSP-S提高组数据结构算法模板大合集的更多相关文章
- 网络安全中机器学习大合集 Awesome
网络安全中机器学习大合集 from:https://github.com/jivoi/awesome-ml-for-cybersecurity/blob/master/README_ch.md#-da ...
- ubuntu 18.04 搭建flask服务器(大合集,个人实操)
ubuntu 18.04 搭建flask服务器(大合集) Ubuntu python flask 服务器 本次使用的Ubuntu版本为:Ubuntu 18.04.5 LTS (GNU/Linux 4. ...
- [题解+总结]NOIP动态规划大合集
1.前言 NOIP2003-2014动态规划题目大合集,有简单的也有难的(对于我这种动态规划盲当然存在难的),今天就把这些东西归纳一下,做一个比较全面的总结,方便对动态规划有一个更深的理解. 2.NO ...
- 直接拿来用!Facebook移动开源项目大合集
直接拿来用!Facebook移动开源项目大合集 时间:2014-04-22 15:37 作者:唐小引 随着iOS依赖管理工具CocoaPods和大量第三方开源库成熟起来,业界积累了大量的优秀开源项目. ...
- NOIP动态规划大合集
1.前言 NOIP2003-2014动态规划题目大合集,有简单的也有难的(对于我这种动态规划盲当然存在难的),今天就把这些东西归纳一下,做一个比较全面的总结,方便对动态规划有一个更深的理解. 2.NO ...
- Lucene搜索方式大合集
package junit; import java.io.File; import java.io.IOException; import java.text.ParseException; imp ...
- 【收藏】Java多线程/并发编程大合集
(一).[Java并发编程]并发编程大合集-兰亭风雨 [Java并发编程]实现多线程的两种方法 [Java并发编程]线程的中断 [Java并发编程]正确挂起.恢复.终止线程 [ ...
- [题解+总结]动态规划大合集II
1.前言 大合集总共14道题,出自江哥之手(这就没什么好戏了),做得让人花枝乱颤.虽说大部分是NOIP难度,也有简单的几道题目,但是还是做的很辛苦,有几道题几乎没思路,下面一道道边看边分析一下. 2. ...
- 从零开始学数据分析,什么程度可以找到工作?( 内附20G、5000分钟数据分析工具教程大合集 )
从零开始学数据分析,什么程度可以找到工作?( 内附20G.5000分钟数据分析工具教程大合集 ) 我现在在Coursera上面学data science 中的R programming,过去很少接 ...
- python字符串操作实方法大合集
python字符串操作实方法大合集,包括了几乎所有常用的python字符串操作,如字符串的替换.删除.截取.复制.连接.比较.查找.分割等,需要的朋友可以参考下: #1.去空格及特殊符号 s.st ...
随机推荐
- itest(爱测试) 开源接口测试,敏捷测试管理平台10.0.1
一:itest work 简介 itest work 开源敏捷测试管理,包含极简的任务管理,测试管理,缺陷管理,测试环境管理,接口测试,接口Mock,还有压测 ,又有丰富的统计分析,8合1工作站.可按 ...
- Nodejs概述 安装Nodejs os模块 path模块 url模块 querystring模块
一.Nodejs概述 介绍 相关网址: https://nodejs.org/zh-cn/ http://nodejs.cn/ Node.js 是一个开源与跨平台的JavaScript 运行时环境.它 ...
- LeetCode 451. Sort Characters By Frequency 根据字符出现频率排序 (C++/Java)
题目: Given a string, sort it in decreasing order based on the frequency of characters. Example 1: Inp ...
- Vulnhub Fall Walkthrough
Recon 二层本地扫描,发现目标靶机. ┌──(kali㉿kali)-[~] └─$ sudo netdiscover -r 192.168.80.0/24 Currently scanning: ...
- MYSQL8存储过程生成日历表以及异常处理
一.环境 数据库:mysql8.0.25 社区版 操作系统:windows 11 ------------------------------------ 二.创建日历表 CREATE TABLE ` ...
- PAT-甲级-1007
一.看题,https://www.patest.cn/contests/pat-a-practise/1007 其实,也是一顿暴力,但是最后一个测试点会运行超时,最开始,计算一段区间的值的总和的时候, ...
- Android发布,全志T507四核A53@1.4GHz工业平台,含税仅168元起!
近年来,Android系统在工业自动化.仪器仪表.医疗.安防等工业领域的使用日趋广泛.为了满足广大工业用户的需求,创龙科技针对全志T507-H工业平台进行了Android系统适配. 创龙科技T507- ...
- IIS部署错误HTTP Error 500.36 ASP.NET Core IIS hosting failure(out-of-process)
错误提示 HTTP Error 500.36 - ASP.NET Core IIS hosting failure (out-of-process) The out of process reques ...
- CF1862C 题解
考虑每个木板在水平放置后对每个位置上产生的贡献. 稍微手玩几组样例: 不难发现一个高度为 \(h\) 的木板在水平放置后会是位置 \([1,h]\) 上高度增加 \(1\). 但是高度最大是 \(10 ...
- yb课堂之用户下单模块开发 《十四》
开发用户下单购买视频接口 VideoOrder模块下单接口开发 VideoOrderController.java package net.ybclass.online_ybclass.control ...