CSP 普及 & 提高 考点 模板合集
零、杂项
加速 cin/cout
:ios::sync_with_stdio(false);
。注:放在 main
函数的第一行,但使用它之后不能使用 scanf/printf
。
避坑/防爆0 指南。
快读:
inline int rd(){
int x = 1, s = 0; char ch = getchar();
while(ch < '0' or ch > '9'){if(ch == '-') x = -1; ch = getchar();}
while(ch >= '0' and ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return x * s;
}
快写:
inline void wr(int x){
if(x < 0) putchar('-'), x *= -1;
if(x > 9) wr(x / 10);
putchar(x % 10 + '0');
}
一、博弈论
Bash/Wythoff/Nimm Game
二、字符串
KMP
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1000010;
int kmp[maxn];
int lena, lenb;
char a[maxn], b[maxn];
int main ()
{
scanf ("%s %s", a + 1, b + 1);
lena = strlen (a + 1), lenb = strlen (b + 1);
for (int i = 2, j = 0; i <= lenb; i++)
{
while (j and b[i] != b[j + 1]) j = kmp[j];
if (b[i] == b[j + 1]) j++;
kmp[i] = j;
}
int j = 0;
for (int i = 1; i <= lena; i++)
{
while (j and b[j + 1] != a[i]) j = kmp[j];
if (b[j + 1] == a[i]) j++;
if (j == lenb)
{
printf ("%d\n", i - lenb + 1);
j = kmp[j];
}
}
for (int i = 1; i <= lenb; i++) printf ("%d ", kmp[i]);
return 0;
}
AC自动机
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
//#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 5;
int n, tot;
int trie[maxn][30], ans[maxn], fail[maxn];
string str, s;
inline void add ()
{
int nw = 0, len = str.size ();
for (int i = 0; i < len; i++)
{
int nxt = str[i] - 'a' + 1;
if (!trie[nw][nxt]) trie[nw][nxt] = ++tot;
nw = trie[nw][nxt];
}
ans[nw]++;
}
queue <int> q;
inline void getfail ()
{
for (int i = 1; i <= 26; i++)
{
if (!trie[0][i]) continue;
fail[trie[0][i]] = 0;
q.push (trie[0][i]);
}
while (!q.empty ())
{
int nw = q.front ();
q.pop ();
for (int i = 1; i <= 26; i++)
{
if (trie[nw][i])
{
fail[trie[nw][i]] = trie[fail[nw]][i];
q.push (trie[nw][i]);
}
else trie[nw][i] = trie[fail[nw]][i];
}
}
}
inline int find ()
{
int len = s.size (), res = 0, nw = 0;
for (int i = 0; i < len; i++)
{
nw = trie[nw][s[i] - 'a' + 1];
for (int j = nw; j and ans[j] != -1; j = fail[j])
{
res += ans[j];
ans[j] = -1;
}
}
return res;
}
int main ()
{
scanf ("%d", &n);
for (int i = 1; i <= n; i++)
{
cin >> str;
add ();
}
getfail ();
cin >> s;
printf ("%d\n", find ());
return 0;
}
字符串哈希
单哈希:洛谷P3370 【模板】字符串哈希
code by 皎月半洒花。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
typedef unsigned long long ull;
ull base=131;
ull a[10010];
char s[10010];
int n,ans=1;
int prime=233317;
ull mod=212370440130137957ll;
ull hashe(char s[])
{
int len=strlen(s);
ull ans=0;
for (int i=0;i<len;i++)
ans=(ans*base+(ull)s[i])%mod+prime;
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",s);
a[i]=hashe(s);
}
sort(a+1,a+n+1);
for(int i=1;i<n;i++)
{
if(a[i]!=a[i+1])
ans++;
}
printf("%d",ans);
}
双哈希的应用:洛谷AT_arc099_d [ARC099D] Eating Symbols Hard
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(register int i = a; i <= b; ++i)
typedef long long ll;
const int maxn = 250005;
const int mod1 = 1e9 + 7, mod2 = 998244353;
int n, p[maxn], bs = 121;
char c[maxn];
map<pair<int, int>, int> mp;
pair<int, int> hsh[maxn];
ll ans;
inline int pw(int x, int p, int mod){
int res = 1;
while(p){
if(p & 1)
res = 1ll * res * x % mod;
p /= 2, x = 1ll * x * x % mod;
} return res;
}
inline pair<int, int> gh(int x, int y){
int a, b; a = b = x;
if(y < 0)
a = pw(a, mod1 - 2, mod1), b = pw(b, mod2 - 2, mod2), y *= -1;
return make_pair(pw(a, y, mod1), pw(b, y, mod2));
}
inline pair<int, int> add(pair<int, int> a, pair<int, int> b){
return make_pair((a.first + b.first) % mod1, (a.second + b.second) % mod2);
}
inline pair<int, int> mns(pair<int, int> a, pair<int, int> b){
return make_pair((a.first + mod1 - b.first) % mod1, (a.second + mod2 - b.second) % mod2);
}
inline pair<int, int> tim(pair<int, int> a, pair<int, int> b){
return make_pair((1ll * a.first * b.first) % mod1, (1ll * a.second * b.second) % mod2);
}
int main(){
scanf("%d", &n);
rep(i, 1, n){
char c; cin >> c;
p[i] = p[i - 1];
if(c == '+') hsh[i] = add(hsh[i - 1], gh(bs, p[i]));
if(c == '-') hsh[i] = mns(hsh[i - 1], gh(bs, p[i]));
if(c == '<') hsh[i] = hsh[i - 1], p[i] -= 1;
if(c == '>') hsh[i] = hsh[i - 1], p[i] += 1;
mp[hsh[i]] += 1;
}
rep(i, 1, n)
ans += mp[add(hsh[i - 1], tim(hsh[n], gh(bs, p[i - 1])))],
mp[hsh[i]] -= 1;
printf("%lld\n", ans);
return 0;
}
Trie树(图)
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn = 800005;
int bl[maxn], ch[maxn][30], tot = 1, vis[maxn];
int n, m;
char nam[55];
inline int read ()
{
int x = 1, s = 0;
char ch = getchar ();
while (ch < '0' or ch > '9'){if (ch == '-') x = -1; ch = getchar ();}
while (ch >= '0' and ch <= '9') s = s * 10 + ch - '0', ch = getchar ();
return x * s;
}
inline void insrt ()
{
int p, u = 0, len = strlen (nam);
for (int i = 0; i <= len; i++)
{
if (i == len)
{
bl[u]++;
break;
}
p = nam[i] - 'a' + 1;
if (ch[u][p])
{
u = ch[u][p];
continue;
}
ch[u][p] = ++tot;
u = ch[u][p];
}
}
inline void dl ()
{
int u = 0, p, len = strlen (nam);
for (int i = 0; i < len; i++)
{
p = nam[i] - 'a' + 1;
if (!ch[u][p])
{
printf ("WRONG\n");
return;
}
u = ch[u][p];
}
if (!bl[u])
{
printf ("WRONG\n");
return;
}
if (!vis[u])
{
printf ("OK\n");
vis[u]++;
return;
}
printf ("REPEAT\n");
return;
}
int main ()
{
n = read ();
for (int i = 1; i <= n; i++)
{
scanf ("%s", nam);
insrt ();
}
m = read ();
for (int i = 1; i <= m; i++)
{
scanf ("%s", nam);
dl ();
}
return 0;
}
manacher
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
//#define int long long
const int maxn = 11000005;
char st[maxn * 2];
int len[maxn * 2], cnt, ans;
int mx, po;
inline void input ()
{
char ch = getchar ();
st[0] = '~', st[cnt = 1] = '#';
while (ch < 'a' or ch > 'z') ch = getchar ();
while (ch >= 'a' and ch <= 'z') st[++cnt] = ch, st[++cnt] = '#', ch = getchar ();
}
signed main ()
{
input ();
for (int i = 1; i <= cnt; ++i)
{
if (i <= mx) len[i] = min (mx - i + 1, len[2 * po - i]);
else len[i] = 1;
while (st[i - len[i]] == st[i + len[i]]) len[i]++;
if (len[i] + i > mx) mx = len[i] + i - 1, po = i;
ans = max (ans, len[i]);
}
printf ("%d\n", ans - 1);
return 0;
}
【To Do】最小表示法
图
最短路
spfa (判负环)
考场慎用,最慢
判负环:记录被松弛次数,如果某一节点的次数
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
//const long long inf = 2147483647;
const int N = 1e5 + 10, M = 5e5 + 10;
int n, st, ed, dis[N];
int wt[M], hd[M], nxt[M], edg[M], idx;
bool vis[N];
void spfa()
{
memset(dis, 0x3f, sizeof dis);
dis[st] = 0;
queue <int> q;
q.push(st);
//vis[st] = true;
while (!q.empty())
{
int tt = q.front();
q.pop();
vis[tt] = false;
for (int i = hd[tt]; i; i = nxt[i])
{
if (dis[tt] + wt[i] < dis[edg[i]])
{
dis[edg[i]] = dis[tt] + wt[i];
if (!vis[edg[i]])
{
q.push(edg[i]);
//vis[edg[i]] = true;
}
}
}
}
}
void add(int a, int b, int c)
{
wt[++idx] = c;
edg[idx] = b;
nxt[idx] = hd[a];
hd[a] = idx;
}
int main()
{
//memset(nxt, -1, sizeof nxt);
int a, b, c, t;
cin >> n >> t >> st;
for (int i = 1; i <= t; i++)
{
cin >> a >> b >> c;
add(a, b, c);
}
spfa();
for (int i = 1; i <= n; i++)
if (st == i)cout << 0 << " ";
else if (dis[i] == dis[0])cout << 2147483647 << " ";
else cout << dis[i] << " ";
return 0;
}
dijkstra (堆优化 & 线段树优化)
Code by little_sun.
#include<bits/stdc++.h>
const int MaxN = 100010, MaxM = 500010;
struct edge
{
int to, dis, next;
};
edge e[MaxM];
int head[MaxN], dis[MaxN], cnt;
bool vis[MaxN];
int n, m, s;
inline void add_edge( int u, int v, int d )
{
cnt++;
e[cnt].dis = d;
e[cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt;
}
struct node
{
int dis;
int pos;
bool operator <( const node &x )const
{
return x.dis < dis;
}
};
std::priority_queue<node> q;
inline void dijkstra()
{
dis[s] = 0;
q.push( ( node ){0, s} );
while( !q.empty() )
{
node tmp = q.top();
q.pop();
int x = tmp.pos, d = tmp.dis;
if( vis[x] )
continue;
vis[x] = 1;
for( int i = head[x]; i; i = e[i].next )
{
int y = e[i].to;
if( dis[y] > dis[x] + e[i].dis )
{
dis[y] = dis[x] + e[i].dis;
if( !vis[y] )
{
q.push( ( node ){dis[y], y} );
}
}
}
}
}
int main()
{
scanf( "%d%d%d", &n, &m, &s );
for(int i = 1; i <= n; ++i)dis[i] = 0x7fffffff;
for( register int i = 0; i < m; ++i )
{
register int u, v, d;
scanf( "%d%d%d", &u, &v, &d );
add_edge( u, v, d );
}
dijkstra();
for( int i = 1; i <= n; i++ )
printf( "%d ", dis[i] );
return 0;
}
倍增 floyd
洛谷P3371 【模板】单源最短路径(弱化版) 70 pts.
code by Nemlit.
#include<bits/stdc++.h>
using namespace std;
#define inf 1234567890
#define maxn 10005
inline int read()
{
int x=0,k=1; char c=getchar();
while(c<'0'||c>'9'){if(c=='-')k=-1;c=getchar();}
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*k;
}//快读
int a[maxn][maxn],n,m,s;
inline void floyd()
{
for(int k=1;k<=n;k++)
//这里要先枚举k(可以理解为中转点)
{
for(int i=1;i<=n;i++)
{
if(i==k||a[i][k]==inf)
{
continue;
}
for(int j=1;j<=n;j++)
{
a[i][j]=min(a[i][j],a[i][k]+a[k][j]);
//松弛操作,即更新每两个点之间的距离
//松弛操作有三角形的三边关系推出
//即两边之和大于第三边
}
}
}
}
int main()
{
n=read(),m=read(),s=read();
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
a[i][j]=inf;
}
}
//初始化,相当于memset(a,inf,sizeof(a))
for(int i=1,u,v,w;i<=m;i++)
{
u=read(),v=read(),w=read();
a[u][v]=min(a[u][v],w);
//取min可以对付重边
}
floyd();
a[s][s]=0;
for(int i=1;i<=n;i++)
{
printf("%d ",a[s][i]);
}
return 0;
}
差分约束
#include<bits/stdc++.h>
using namespace std;
int n, m;
int flag = 0;
int ans = -0x7f;
int hd[10005];
int cnt;
int vis[10005];
int dis[10005];
int inque[10005];
struct node{
int to;
int w, nxt;
}e[10005];
void add(int u, int v, int w)
{
e[++cnt].w = w;
e[cnt].to = v;
e[cnt].nxt = hd[u];
hd[u] = cnt;
}
void spfa ()
{
memset (dis, 0x3f3f3f3f, sizeof (dis));
memset (vis, 0, sizeof (vis));
queue<int>q;
dis[0] = 0;
vis[0] = 1;
inque[0] = 1;
q.push(0);
while (!q.empty())
{
int x;
x = q.front();
vis[x] = 0;
q.pop();
for (int i = hd[x]; i; i = e[i].nxt)
{
int v = e[i].to, w = e[i].w;
if (dis[v] > dis[x] + w)
{
dis[v] = dis[x] + w;
if (vis[v] == 0)
{
q.push(v);
vis[v] = 1;
++inque[v];
if (inque[v] > n)
{
cout << "NO" << endl;
flag = 1;
return;
}
}
}
}
}
//int minn;
//for (int i = 1; i <= n; i++)minn = min (minn, dis[i]);
for (int i = 1; i <= n; ++i)cout << dis[i] << " ";
// cout << "NO" << endl;
return;
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= m; i++)
{
int a, b, c;
cin >> a >> b >> c;
add (b, a, c);
}
for (int i = 1; i <= n; i++)add (0, i, 0);
spfa();
return 0;
}
Tarjan
强连通分量/缩点
强连通分量。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 100005;
int n, m;
int cnt, hd[maxn];
struct node{
int to, nxt;
}e[maxn * 2];
int dfn[maxn], low[maxn];
int tmp, top;
int st[maxn];
int co[maxn], col;
void add (int u, int v)
{
e[++cnt].to = v;
e[cnt].nxt = hd[u];
hd[u] = cnt;
}
void tarjan (int u)
{
dfn[u] = low[u] = ++tmp;//step 1
st[++top] = u;//step 2
for (int i = hd[u]; i; i = e[i].nxt)//step 3
{
int v = e[i].to;
if (!dfn[v])//如果没有被访问过
{
tarjan (v);
low[u] = min (low[u], low[v]);
}
else if (!co[v]) //如果它还不在一个强连通分量内
low[u] = min (low[u], dfn[v]);
}
if (low[u] == dfn[u])//step 4
{
co[u] = ++col;
while (st[top] != u)//弹栈操作
{
co[st[top]] = col;
--top;
}
--top;//最后记得把 u 也弹出去
}
}
int vis[maxn];
signed main ()
{
scanf ("%lld %lld", &n, &m);
for (int i = 1; i <= m; i++)
{
int u, v;
scanf ("%lld %lld", &u, &v);
add (u, v);
}
for (int i = 1; i <= n; i++)
{
if (!dfn[i]) tarjan (i);
}
printf ("%lld\n", col);
for (int i = 1; i <= n; i++)
{
if (vis[i]) continue;
printf ("%lld ", i);
vis[i] = 1;
for (int j = i + 1; j <= n; j++)
{
if (co[j] == co[i])
{
printf ("%lld ", j);
vis[j] = 1;
}
}
printf ("\n");
}
return 0;
}
缩点:P3387 【模板】缩点.
#include<bits/stdc++.h>
using namespace std;
const int maxn = 150005;
int n, m;
int cnt, hd[maxn];
struct node{
int to, nxt;
}e[maxn * 2];
int dfn[maxn], low[maxn];
int top, st[maxn], de[maxn], si[maxn];
int col, co[maxn];
int x[maxn], y[maxn];
int tmp, ans;
int f[maxn], sum[maxn];
int w[maxn];
void add (int u, int v)
{
e[++cnt].to = v;
e[cnt].nxt = hd[u];
hd[u] = cnt;
}
void tarjan (int u)
{
dfn[u] = low[u] = ++tmp;
st[++top] = u;
for (int i = hd[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (!dfn[v])
{
tarjan (v);
low[u] = min (low[u], low[v]);
}
else if (!co[v]) low[u] = min (low[u], dfn[v]);
}
if (dfn[u] == low[u])
{
co[u] = ++col;
sum[col] += w[u];
while (st[top] != u)
{
co[st[top]] = col;
sum[col] += w[st[top]];
--top;
}
--top;
}
}
void search (int u)
{
if (f[u]) return;
f[u] = sum[u];
int maxsum = 0;
for (int i = hd[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (!f[v]) search (v);
maxsum = max (maxsum, f[v]);
}
f[u] += maxsum;
}
int main ()
{
scanf ("%d %d", &n, &m);
for (int i = 1; i <= n; i++) scanf ("%d", &w[i]);
for (int i = 1; i <= m; i++)
{
scanf ("%d %d", &x[i], &y[i]);
add (x[i], y[i]);
}
for (int i = 1; i <= n; i++)
{
if (!dfn[i]) tarjan (i);
}
cnt = 0;
memset (hd, 0, sizeof hd);
memset (e, 0, sizeof e);
for (int i = 1; i <= m; i++)
{
if (co[x[i]] != co[y[i]])
{
add (co[x[i]], co[y[i]]);
}
}
for (int i = 1; i <= col; i++)
{
if (!f[i])
{
search (i);
ans = max (ans, f[i]);
}
}
printf ("%d\n", ans);
return 0;
}
点双边双
点双:
void tarjan (int u, int f)
{
dfn[u] = low[u] = ++tmp;
st[++top] = u;
co[u] = 1;
if (u == rt and !hd[u])
{
cout << u << endl;
return;
}
for (int i = hd[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (!dfn[v])
{
tarjan (v, u);
low[u] = min (low[u], low[v]);
if (dfn[u] <= low[v])
{
col++;
while (st[top] != u)
{
cout << st[top] << " ";
co[st[top]] = 0;
if (st[top] == v) {top--;break;}
top--;
}
cout << u << endl;
}
}
else if (v != f) low[u] = min (low[u], dfn[v]);
}
}
边双:
inline void tarjan (int u)
{
dfn[u] = low[u] = ++tmp;
st[++top] = u;
for (int i = hd[u]; i; i = e[i].nxt)
if (!vis[i])
{
int v = e[i].to;
vis[i] = vis[i ^ 1] = 1;
if (!dfn[v])
{
tarjan (v);
low[u] = min (low[u], low[v]);
}
else low[u] = min (low[u], dfn[v]);
}
if (dfn[u] == low[u])//是环就直接倒出来
{
co[u] = ++col;
while (st[top] != u)
{
co[st[top]] = col;
top--;
}
top--;
}
}
割点割边、桥
割点:
void tarjan (int u)
{
dfn[u] = low[u] = ++tmp;
int tot = 0;
for (int i = hd[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (!dfn[v])
{
tot++;
tarjan (v);
low[u] = min (low[u], low[v]);
if ((u == root and tot > 1) or (u != root and dfn[u] <= low[v])) vis[u] = 1;
}
else low[u] = min (low[u], dfn[v]);
}
}
割边(桥):
void tarjan (int u, int li)
{
dfn[u] = low[u] = ++tmp;
for (int i = hd[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (!dfn[v])
{
tarjan (v, i);
low[u] = min (low[u], low[v]);
if (dfn[u] < low[v]) ans[++cntn] = (abc){min (u, v), max (u, v)};
}
else if (i != find (li)) low[u] = min (low[u], dfn[v]);
}
}
2-sat
拓扑排序
洛谷P1137 旅行计划。
#include<bits/stdc++.h>
using namespace std;
#define rint register int
const int maxn = 2e5 + 5;
int n, m;
int dis[maxn], r[maxn];
vector <int> e[maxn];
inline int read ()
{
int s = 0, x = 1;
char ch = getchar ();
while (ch < '0' or ch > '9')
{
if (ch == '-') x = -1;
ch = getchar ();
}
while (ch >= '0' and ch <= '9')
s = s * 10 + ch - '0', ch = getchar ();
return x * s;
}
inline void topo ()
{
queue <int> q;
for (rint i (1); i <= n; ++i)
if (!r[i])
q.push (i), dis[i] = 1;
while (!q.empty ())
{
int u = q.front ();
q.pop ();
for (rint i (0); i < e[u].size (); ++i)
{
int v = e[u][i];
r[v] -= 1;
if (!r[v])
{
q.push (v);
dis[v] = dis[u] + 1;
}
}
}
}
int main ()
{
n = read (), m = read ();
for (rint i (1); i <= m; ++i)
{
int u, v;
u = read (), v = read ();
e[u].push_back (v);
r[v] += 1;
}
topo ();
for (rint i (1); i <= n; ++i)
printf ("%d\n", dis[i]);
return 0;
}
割边:
void tarjan (int u, int li)
{
dfn[u] = low[u] = ++tmp;
for (int i = hd[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (!dfn[v])
{
tarjan (v, i);
low[u] = min (low[u], low[v]);
if (dfn[u] < low[v]) ans[++cntn] = (abc){min (u, v), max (u, v)};
}
else if (i != find (li)) low[u] = min (low[u], dfn[v]);
}
}
二分图(最大匹配-匈牙利 & 最大权匹配-KM)
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
#define rep(i, a, b) for(int i = a; i <= b; ++i)
#define maxn 1005
int n, m, c, ans;
vector <int> e[maxn];
int mtc[maxn], vis[maxn];
inline bool match(int u, int wh)
{
if(vis[u] == wh) return 0;//避免死循环
vis[u] = wh;
if(!e[u].size()) return 0;//注意边界
rep(i, 0, e[u].size() - 1)
if(!mtc[e[u][i]] or match(mtc[e[u][i]], wh))
{
mtc[e[u][i]] = u;
return 1;
}
return 0;
}
int main()
{
scanf("%d %d %d", &n, &m, &c);
rep(i, 1, c)
{
int u, v;
scanf("%d %d", &u, &v);
e[u].push_back(v);
}
rep(i, 1, n)
ans += match(i, i);
printf("%d\n", ans);
return 0;
}
网络流(大概不考,熟悉板子即可)
分数规划(提高考)
欧拉图
以下内容皆出自 (LG)Marsrayd。
#include <bits/stdc++.h>
using namespace std;
const int MAX=100010;
int n,m,u,v,del[MAX];
int du[MAX][2];//记录入度和出度
stack <int> st;
vector <int> G[MAX];
void dfs(int now)
{
for(int i=del[now];i<G[now].size();i=del[now])
{
del[now]=i+1;
dfs(G[now][i]);
}
st.push(now);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++) scanf("%d%d",&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; //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/*出度比入度多1*/) cnt[1]++,S=i;
if(du[i][0]-du[i][1]==1/*入度比出度多1*/) cnt[0]++;
}
if((!flag)&&!(cnt[0]==cnt[1]&&cnt[0]==1)) return !printf("No");
//不满足欧拉回路的判定条件,也不满足欧拉路径的判定条件,直接输出"No"
dfs(S);
while(!st.empty()) printf("%d ",st.top()),st.pop();
return 0;
}
树
最小生成树
Kruskal(生成树) & Prim 堆优化
kruskal:
#include<bits/stdc++.h>
using namespace std;
int n, m;
int father[5005];
struct node{
int x, y;
int w;
}a[200005];
int cmp(node x, node y)
{
return x.w < y.w;
}
int find (int x)
{
if (father[x] != x)father[x] = find(father[x]);
return father[x];
}
void unionn (int x, int y)
{
father[find(y)] = find(x);
}
bool judge (int x, int y)
{
if (find(x) == find(y))return 1;
else return 0;
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)father[i] = i;
for (int i = 1; i <= m; i++)
{
cin >> a[i].x >> a[i].y >> a[i].w;
}
sort (a + 1, a + m + 1, cmp);
int tot = 0;
int cnt = 0;
for (int i = 1; i <= m; i++)
{
if (judge(a[i].x, a[i].y) == 0)
{
unionn (a[i].x, a[i].y);
cnt += 1;
tot += a[i].w;
}
if (cnt == (n - 1))break;
}
if (cnt != (n - 1))cout << "orz";
else cout << tot;
return 0;
}
prim:
//prim
#include<bits/stdc++.h>
using namespace std;
int n;
int a[105][105];//邻接矩阵
int minn[105];//存放每次邻接边权最小值
bool u[105];//判断是否在图中
int ttl = 0;//total,最终答案
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)cin >> a[i][j];
memset(minn, 0x7f, sizeof(minn));
memset(u, 1, sizeof(u));//皆不在图中
minn[1] = 0;
for (int i = 2; i <= n; i++)
{
int k = 0;//寻找当前最小的一个蓝点
for (int j = 1; j <= n; j++)
{
if(u[j] && (minn[j] < minn[k]))k=j;//打擂台
}
u[k] = false;//连入图中
for (int j = 1; j <= n; j++)//刷新其他蓝点的最小权值
if(u[j] && (a[k][j] < minn[j]))
minn[j] = a[k][j];
}
for (int i = 1; i <= n; i++)ttl += minn[i];//计算总和
cout<<ttl;
return 0;
}
(严格)次小生成树
最大生成树
【选】最优比率、最小瓶颈生成树
LCA
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(register int i = a; i <= b; ++i)
#define ls t[x].ch[0]
#define rs t[x].ch[1]
const int maxn = 3e5 + 5;
int n, m;
struct node{
int f, ch[2], sum, val;
bool rev;
}t[maxn];
inline bool nrt(int p)
{
int x = t[p].f;
return (ls == p or rs == p);
}
inline void up(int x) {t[x].sum = t[ls].sum ^ t[rs].sum ^ t[x].val;}
inline void rv(int x) {swap(ls, rs); t[x].rev ^= 1;}
inline void dw(int x)
{
if(!t[x].rev) return;
if(ls) rv(ls); if(rs) rv(rs);
t[x].rev = 0;
}
inline void rotate(int x)
{
int y = t[x].f, z = t[y].f;
int kx = (t[y].ch[1] == x), ky = (t[z].ch[1] == y), w = t[x].ch[!kx];
if(nrt(y)) t[z].ch[ky] = x;
t[x].ch[!kx] = y, t[y].ch[kx] = w;
if(w) t[w].f = y;
t[y].f = x, t[x].f = z;
up(y), up(x);
}
inline void pushall(int x) {if(nrt(x)) pushall(t[x].f); dw(x);}
inline void splay(int x)
{
pushall(x);
while(nrt(x))
{
int y = t[x].f, z = t[y].f;
if(nrt(y)) rotate((t[z].ch[1] == y) ^ (t[y].ch[1] == x) ? x : y);
rotate(x);
}
up(x);
}
inline void access(int x)
{
for(register int lst = 0; x; x = t[lst = x].f)
splay(x), rs = lst, up(x);
}
inline void mkrt(int x) {access(x), splay(x), rv(x);}
inline int fndrt(int x)
{
access(x), splay(x);
while(ls) dw(x), x = ls;
splay(x); return x;
}
inline void split(int x, int y) {mkrt(x), access(y), splay(y);}
inline void link(int x, int y) {mkrt(x); if(fndrt(y) != x) t[x].f = y;}
inline void cut(int x, int y)
{
split(x, y);
if(fndrt(y) == x and t[y].f == x and !t[y].ch[0])
t[y].f = t[x].ch[1] = 0, up(x);
}
int main()
{
scanf("%d%d", &n, &m);
rep(i, 1, n) scanf("%d", &t[i].val);
rep(i, 1, m)
{
int opt, x, y;
scanf("%d%d%d", &opt, &x, &y);
if(!opt) split(x, y), printf("%d\n", t[y].sum);
if(opt == 1) link(x, y);
if(opt == 2) cut(x, y);
if(opt == 3) splay(x), t[x].val = y;
}
return 0;
}
基环树
树链剖分
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n, m, rt;
int p;
const int maxn = 200005;
int cnt, hd[maxn];
struct node{
int nxt, to;
}e[maxn * 2];
int wt[maxn], w[maxn];//new & old number's val
struct tree{
int l, r;
int lz,sum;
}t[4 * maxn];
int tmp;
int dep[maxn], fa[maxn];
int siz[maxn], son[maxn];
int top[maxn], id[maxn];
//------------------------------
inline void add (int u, int v)
{
e[++cnt].to = v;
e[cnt].nxt = hd[u];
hd[u] = cnt;
}
//------------------------------
inline void dfs1 (int x, int fth)
{
dep[x] = dep[fth] + 1;
fa[x] = fth;
siz[x] = 1;
int maxss = -1;
for (int i = hd[x]; i; i = e[i].nxt)
{
int v = e[i].to;
if (v == fth) continue;
dfs1 (v, x);
siz[x] += siz[v];
if (siz[v] > maxss)
{
maxss = siz[v];
son[x] = v;
}
}
}
inline void dfs2 (int x, int tpx)
{
id[x] = ++tmp;
wt[tmp] = w[x];
top[x] = tpx;
if (!son[x]) return;
dfs2 (son[x], tpx);
for (int i = hd[x]; i; i = e[i].nxt)
{
int v = e[i].to;
if (v == fa[x] or v == son[x]) continue;
dfs2 (v, v);
}
}
//------------------------------
inline void build (int i, int l, int r)
{
t[i].l = l, t[i].r = r;
if (l == r)
{
t[i].sum = wt[l];
if (t[i].sum > p) t[i].sum %= p;
return;
}
int mid = (l + r) >> 1;
build (i << 1, l, mid);
build (i << 1 | 1, mid + 1, r);
t[i].sum = t[i << 1].sum + t[i << 1 | 1].sum;
t[i].sum %= p;
return;
}
inline void push_down (int i)
{
if (t[i].lz)
{
t[i << 1].lz += t[i].lz;
t[i << 1].lz %= p;
t[i << 1 | 1].lz += t[i].lz;
t[i << 1 | 1].lz %= p;
//int lenn = t[i].r - t[i].l + 1;
//t[i << 1].sum += (t[i].lz * (lenn - (lenn >> 1)));
//t[i << 1 | 1].sum += (t[i].lz * (lenn >> 1));
int lenl, lenr;
lenl = t[i << 1].r - t[i << 1].l + 1;
lenr = t[i << 1 | 1].r - t[i << 1 | 1].l + 1;
t[i << 1].sum += (t[i].lz * lenl);
t[i << 1 | 1].sum += (t[i].lz * lenr);
t[i << 1].sum %= p;
t[i << 1 | 1].sum %= p;
}
t[i].lz = 0;
return;
}
inline void updt (int i, int l, int r, int k)
{
if (t[i].l >= l and t[i].r <= r)
{
t[i].lz += k;
int len = t[i].r - t[i].l + 1;
t[i].sum += (k * len);
t[i].sum %= p;
return;
}
push_down (i);
if (t[i << 1].r >= l) updt (i << 1, l, r, k);
if (t[i << 1 | 1].l <= r) updt (i << 1 | 1, l, r, k);
t[i].sum = t[i << 1].sum + t[i << 1 | 1].sum;
t[i].sum %= p;
return;
}
inline int query (int i, int l, int r)
{
if (t[i].l >= l and t[i].r <= r) return t[i].sum % p;
push_down (i);
int s = 0;
if (t[i << 1].r >= l) s += query (i << 1, l, r);
if (t[i << 1 | 1].l <= r) s += query (i << 1 | 1, l, r);
s %= p;
return s;
}
//------------------------------
inline int qrange (int x, int y)
{
int ans = 0;
while (top[x] != top[y])
{
if (dep[top[x]] < dep[top[y]]) swap (x, y);
ans += query (1, id[top[x]], id[x]);
ans %= p;
x = fa[top[x]];
}
if (dep[x] > dep[y]) swap (x, y);
ans += query (1, id[x], id[y]);
ans %= p;
return ans;
}
inline void updtrange (int x, int y, int k)
{
k %= p;
while (top[x] != top[y])
{
if (dep[top[x]] < dep[top[y]]) swap (x, y);
updt (1, id[top[x]], id[x], k);
x = fa[top[x]];
}
if (dep[x] > dep[y]) swap (x, y);
updt (1, id[x], id[y], k);
}
inline int qson (int x)
{
return query (1, id[x], id[x] + siz[x] - 1);
}
inline void uson (int x, int k)
{
updt (1, id[x], id[x] + siz[x] - 1, k);
}
//------------------------------
signed main ()
{
scanf ("%lld%lld%lld%lld", &n, &m, &rt, &p);
for (int i = 1; i <= n; i++) scanf ("%lld", &w[i]);
for (int i = 1; i < n; i++)
{
int u, v;
scanf ("%lld%lld", &u, &v);
add (u, v), add (v, u);
}
dfs1 (rt, 0);
dfs2 (rt, rt);
build (1, 1, n);
while (m--)
{
int k, x, y, z;
scanf ("%lld", &k);
if (k == 1)
{
scanf ("%lld%lld%lld", &x, &y, &z);
updtrange (x, y, z);
}
else if (k == 2)
{
scanf ("%lld%lld", &x, &y);
printf ("%lld\n", qrange (x, y));
}
else if (k == 3)
{
scanf ("%lld%lld", &x, &y);
uson (x, y);
}
else if (k == 4)
{
scanf ("%lld", &x);
printf ("%lld\n", qson (x));
}
}
return 0;
}
括号序列
dfs序
树上倍增
树的直径、树的重心
树的直径:
一下均摘自 OI-Wiki。
法1:使用两次 dfs。
const int N = 10000 + 10;
int n, d = 0;
int d1[N], d2[N];
vector<int> E[N];
void dfs(int u, int fa) {
d1[u] = d2[u] = 0;
for (int v : E[u]) {
if (v == fa) continue;
dfs(v, u);
int t = d1[v] + 1;
if (t > d1[u])
d2[u] = d1[u], d1[u] = t;
else if (t > d2[u])
d2[u] = t;
}
d = max(d, d1[u] + d2[u]);
}
int main() {
scanf("%d", &n);
for (int i = 1; i < n; i++) {
int u, v;
scanf("%d %d", &u, &v);
E[u].push_back(v), E[v].push_back(u);
}
dfs(1, 0);
printf("%d\n", d);
return 0;
}
法2:树形 dp(最远距离和次远距离)。
const int N = 10000 + 10;
int n, d = 0;
int d1[N], d2[N];
vector<int> E[N];
void dfs(int u, int fa) {
d1[u] = d2[u] = 0;
for (int v : E[u]) {
if (v == fa) continue;
dfs(v, u);
int t = d1[v] + 1;
if (t > d1[u])
d2[u] = d1[u], d1[u] = t;
else if (t > d2[u])
d2[u] = t;
}
d = max(d, d1[u] + d2[u]);
}
int main() {
scanf("%d", &n);
for (int i = 1; i < n; i++) {
int u, v;
scanf("%d %d", &u, &v);
E[u].push_back(v), E[v].push_back(u);
}
dfs(1, 0);
printf("%d\n", d);
return 0;
}
树的重心
// 这份代码默认节点编号从 1 开始,即 i ∈ [1,n]
int size[MAXN], // 这个节点的“大小”(所有子树上节点数 + 该节点)
weight[MAXN], // 这个节点的“重量”
centroid[2]; // 用于记录树的重心(存的是节点编号)
void GetCentroid(int cur, int fa) { // cur 表示当前节点 (current)
size[cur] = 1;
weight[cur] = 0;
for (int i = head[cur]; i != -1; i = e[i].nxt) {
if (e[i].to != fa) { // e[i].to 表示这条有向边所通向的节点。
GetCentroid(e[i].to, cur);
size[cur] += size[e[i].to];
weight[cur] = max(weight[cur], size[e[i].to]);
}
}
weight[cur] = max(weight[cur], n - size[cur]);
if (weight[cur] <= n / 2) { // 依照树的重心的定义统计
centroid[centroid[0] != 0] = cur;
}
}
数论
- 埃氏筛、线性筛
- 欧拉函数
- 欧拉定理
- 线性筛欧拉函数
- sqrt(n)求单个值的欧拉函数
- exgcd
- 求逆元
- 求同余方程
- 求
- 中国剩余定理-互质版
- 矩阵快速幂
- 容斥原理-Ramsey定理
- 费马小定理
- 逆元(线性求、exgcd求、费马小定理求)
- 高斯消元
- 线性基
- 排列组合-杨辉三角
- 斐波那契数列-
- 卡特兰数
- 【选】斯特林数、贝尔数
- 概率 & 期望
动态规划
- 简单 dp
- 背包 dp
- 01 背包
- 完全背包
- 多重背包
- 区间 dp
- 状压 dp (普通状压 & 枚举子集 dp)
- 数位 dp
- 树形 dp (基环树 dp)
- 环形 dp
- 环 + 外向树上的 dp
- 期望 dp
- dp 套 dp
- 优化
- 【选】四边形不等式
- 单调队列、线段树
数据结构
- 带权并查集
- 哈希表
- 双向列表
- st 表
- 树状数组 (求逆序对 & 多维)
- 线段树
- 动态开点 & 线段树的合并
- 权值线段树
- 【选】zkw 线段树
- 二维线段树
- 主席树 (静态 & 动态 第
大) - 扫描线
- 平衡树 (splay、treap、无旋 treap)
- trie 树
- STL((multi)set)
搜索
- bfs/dfs
- 双向 bfs
其他算法思想
- 二分、三分
- 倍增
- 贪心
- 分治
- 离散化
- 模拟
- 排序(重载运算符)
- 分块
- (二维)前缀和
- (压位)高精度
- 矩阵加速递推
- 打表
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】