存图方法
全文使用的都是一种比较新型的存图方式——链式前向星。这种方式优势和劣势都不明显,属于是比较中庸的一种方式。正因为很中庸,所以可以闭着眼用,一般不会出问题,我很喜欢。
struct node {
int to, next, w;
}edge[maxn << 2];
int head[maxn], cnt;
其中 e d g e [ i ] . t o edge[i].to edge[i].to表示第 i i i条边的终点, e d g e [ i ] . n e x t edge[i].next edge[i].next表示与第i条边同起点的下一条边的存储位置, e d g e [ i ] . w edge[i].w edge[i].w为边权值。
另外还有一个数组 h e a d [ ] head[] head[],它是用来储存以 i i i为起点的第一条边存储的位置,实际上这里的第一条边存储的位置其实在以 i i i为起点的所有边的最后输入的那条边的编号。
边的添加
void add(int u, int v, int w) {
edge[++cnt].to = v;
edge[cnt].w = w;
edge[cnt].next = head[u];
head[u] = cnt;
}
如果是无向图的话记得要加两次边,还要加一次 a d d ( v , u , w ) add(v,\ u,\ w) add(v, u, w)
图的遍历
bool vis[maxn];
void dfs(int x) {
vis[x] = true;
for(int i = x; i; i = edge[i].next) {
if(vis[edge[i].to]) continue;
dfs(edge[i].to);
}
}
最短路
多源最短路
Floyed
#include <iostream>
using namespace std;
#define maxn 305
int plat[maxn][maxn];
int tmpa, tmpb, tmpc, n, m, q;
void floyd() {
for(register int k = 1; k <= n; ++k)
for(register int i = 1; i <= n; ++i)
for(register int j = 1; j <= n; ++j)
if(i != j && i != k && j != k)
plat[i][j] = std::min(plat[i][j], plat[i][k] + plat[k][j]);
}
int main() {
cin >> n >> m;
for(register int i = 1; i <= m; ++i) {
cin >> tmpa >> tmpb >> tmpc;
plat[tmpa][tmpb] = tmpc;
plat[tmpb][tmpa] = tmpc;
}
floyd();
cin >> q;
for(register int i = 1; i <= n; ++i) {
cin >> tmpa >> tmpb;
cout << plat[tmpa][tmpb] << endl;
}
}
单源最短路
堆优化的Dijkstra
在有负权边的时候不能用。当然你把所有的边加成正的也不是不行。
#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
const int maxn = 100005;
struct node {
int to, next, w;
}edge[maxn << 2];
struct node1 {
int u, d;
bool operator < (const node1 &a)const {
return d > a.d;
}
};
int head[maxn], cnt, dist[maxn];
int n, m, s;
void add(int u, int v, int w) {
edge[++cnt].to = v;
edge[cnt].w = w;
edge[cnt].next = head[u];
head[u] = cnt;
}
void Dijkstra(int x) {
for(int i = 1; i <= n; i++) dist[i] = 2147483647; dist[x] = 0;
priority_queue<node1> Q;
Q.push((node1){x, 0});
while(!Q.empty()) {
node1 a = Q.top();
Q.pop();
int uu = a.u;
int dd = a.d;
if(dd != dist[uu]) continue;
for(int i = head[uu]; i; i = edge[i].next) {
int vv = edge[i].to;
int ww = edge[i].w;
if(dist[vv] > dist[uu] + ww) {
dist[vv] = dist[uu] + ww;
Q.push((node1){vv, dist[vv]});
}
}
}
}
int main() {
cin >> n >> m >> s;
for(int i = 1; i <= m; i++) {
int tmpa, tmpb, tmpc;
cin >> tmpa >> tmpb >> tmpc;
add(tmpa, tmpb, tmpc);
}
Dijkstra(s);
for(int i = 1; i <= n; i++) cout << dist[i] << " ";
cout << endl;
return 0;
}
利用优先队列可以在 l o g n logn logn的时间内找到最短的边,大大加快了最短路运行的时间,整体时间复杂度降到 n l o g n nlogn nlogn。
优先队列要重载。默认是从大到小,要从小到大需要重载运算符。
SPFA
容易死。
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
#define maxn 10005
#define maxm 30005
struct node {
int to, nxt, w;
}edge[maxm << 1];
int head[maxn], cnt, n, m, tmpa, tmpb, tmpc;
int dist[maxn], q;
bool vis[maxn];
void add(int u, int v, int w) {
edge[++cnt].to = v;
edge[cnt].nxt = head[u];
edge[cnt].w = w;
head[u] = cnt;
}
void spfa(int s) {
memset(vis, false, sizeof(vis));
for(register int i = 1; i <= n; ++i)
dist[i] = 2147483647;
queue<int> Q;
dist[s] = 0, vis[s] = true;
Q.push(s);
while(!Q.empty()) {
int u = Q.front();
Q.pop(), vis[u] = false;
for(register int i = head[u]; i; i = edge[i].nxt) {
int v = edge[i].to, w = edge[i].w;
if(dist[v] > dist[u] + w) {
dist[v] = dist[u] + w;
if(!vis[v]) {
vis[v] = true;
Q.push(v);
}
}
}
}
}
int main() {
cin >> n >> m;
for(register int i = 1; i <= m; ++i) {
cin >> tmpa >> tmpb >> tmpc;
add(tmpa, tmpb, tmpc);
add(tmpb, tmpa, tmpc);
}
cin >> q;
for(register int i = 1; i <= q; ++i) {
cin >> tmpa >> tmpb;
spfa(tmpa);
cout << dist[tmpb] << std::endl;
}
}
跟优先队列优化的 D i j k s t r a Dijkstra Dijkstra差不多。但是这个可以判断负环。怎么判断?如果某个点进队的次数甚至大于了 n n n那就说明肯定有负环
#include <iostream>
#include <stdio.h>
#include <queue>
#include <cstring>
#define maxn 100005
using namespace std;
struct node {
int to, nxt, w;
}edge[maxn >> 1];
int head[maxn], cnt, appCount[maxn];
bool vis[maxn], flag;
void add(int u, int v, int w) {
edge[++cnt].to = v;
edge[cnt].w = w;
edge[cnt].nxt = head[u];
head[u] = cnt;
}
int n, m, s, dist[maxn], t;
void SPFA(int s) {
queue<int> Lily;
for(register int i = 1; i <= n; ++i)
dist[i] = 2147483647;
memset(vis, false, sizeof(vis));
dist[s] = 0;
Lily.push(s);
vis[s] = true;
appCount[s] = 1;
while(!Lily.empty()) {
int f = Lily.front();
Lily.pop();
vis[f] = false;
for(register int i = head[f]; i; i = edge[i].nxt) {
int v = edge[i].to;
int w = edge[i].w;
if(dist[v] > dist[f] + w) {
dist[v] = dist[f] + w;
appCount[v] = appCount[f] + 1;
if(appCount[v] > n) {
flag = true;
return;
}
if(!vis[v])
Lily.push(v);
}
}
}
}
void solve() {
memset(head, 0, sizeof(head));
memset(vis, false, sizeof(vis));
memset(appCount, 0, sizeof(appCount));
flag = false;
cnt = 0;
cin >> n >> m;
int tmpa, tmpb, tmpc;
for(register int i = 1; i <= m; ++i) {
cin >> tmpa >> tmpb >> tmpc;
add(tmpa, tmpb, tmpc);
if(tmpc >= 0)
add(tmpb, tmpa, tmpc);
}
SPFA(1);
if(flag) cout << "YE5" << endl;
else cout << "N0" << endl;
return;
}
int main() {
cin >> t;
memset(head, 0, sizeof(head));
memset(vis, false, sizeof(vis));
memset(appCount, 0, sizeof(appCount));
cnt = 0;
while(t--) solve();
return 0;
}
最小生成树
优先队列优化的Prim
#include <iostream>
#include <queue>
using namespace std;
const int maxn = 100005;
const int inf = 2147483647;
struct node {
int to, next, w;
}edge[maxn << 2];
struct node1 {
int u, d;
bool operator < (const node1 &a)const {
return d > a.d;
}
};
int head[maxn], cnt;
int dis[maxn], n, m, tot, ans;
bool vis[maxn];
void add(int u, int v, int w) {
edge[++cnt].to = v;
edge[cnt].next = head[u];
edge[cnt].w = w;
head[u] = cnt;
}
inline int min(int a, int b) {
if(a < b) return a;
else return b;
}
priority_queue<node1> Q;
void Prim() {
for(int i = 1; i <= n; i++) dis[i] = inf;
dis[1] = 0;
Q.push((node1){1, 0});
while(!Q.empty() && tot < n) {
node1 a = Q.top();
Q.pop();
int uu = a.u;
int dd = a.d;
if(vis[uu]) continue;
tot++;
ans += dd;
vis[uu] = true;
for(int i = head[uu]; i; i = edge[i].next)
if(dis[edge[i].to] > edge[i].w) {
dis[edge[i].to] = edge[i].w;
Q.push((node1){edge[i].to, dis[edge[i].to]});
}
}
}
int main() {
cin >> n >> m;
for(int i = 1; i <= m; i++) {
int tmpa, tmpb, tmpc;
cin >> tmpa >> tmpb >> tmpc;
add(tmpa, tmpb, tmpc);
add(tmpb, tmpa, tmpc);
}
Prim();
if(tot < n) {
cout << "orz" << endl;
return 0;
}
cout << ans << endl;
return 0;
}
Kruskal
就是贪心选择最小的点,没啥好说的。
用并查集维护。
#include <iostream>
#include <algorithm>
using namespace std;
#define maxn 10005
#define maxm 30005
struct node {
int x, y, w;
}edge[maxm];
int n, m, tmpa, tmpb, tmpc, ans;
int f[maxn];
bool cmp(node a, node b) {
return a.w < b.w;
}
int find(int x) {
if(x == f[x]) return x;
f[x] = find(f[x]);
return f[x];
}
int main() {
cin >> n >> m;
for(register int i = 1; i <= m; ++i) {
cin >> tmpa >> tmpb >> tmpc;
edge[i].x = tmpa;
edge[i].y = tmpb;
edge[i].w = tmpc;
}
for(register int i = 1; i <= n; ++i)
f[i] = i;
sort(edge + 1, edge + 1 + n, cmp);
int k = 0;
for(register int i = 1; i <= m; ++i) {
int a = find(edge[i].x);
int b = find(edge[i].y);
if(a == b) continue;
ans += edge[i].w;
k++;
f[a] = b;
f(k == n - 1) break;
}
if(k < n - 1) {
cout << "????" << endl;
return 0;
}
cout << ans << endl;
return 0;
}
另外,关于次小生成树:
先求最小生成树 T T T,枚举添加不在 T T T中的边,则添加后一定会形成环,找到环上边值第二大的边,把它删掉,计算当前生成树的权值,取所有枚举修改的生成树的最小值,即为次小生成树。这种方法的实现更为简单,首先求最小生成树T,然后从每个结点 u u u,遍历最小生成树 T T T,用一个二维的数组 m a x [ u ] [ v ] max[u][v] max[u][v]记录结点u到结点v的路径上边的最大值,然后枚举不在T中的边 ( u , v ) (u,v) (u,v),计算 T − m a x [ u ] [ v ] + w ( u , v ) T-max[u][v]+w(u,v) T−max[u][v]+w(u,v)的最小值,即为次小生成树的权值 ,这种方法的时间复杂度为 O ( n 2 + e ) O(n^2+e) O(n2+e)
Tarjan
我用的主要作用就是缩点。
缩点咯
#include <iostream>
#include <stdio.h>
using namespace std;
#define maxn 100005
#define maxm 300005
inline int min(int a, int b) {
if(a < b) return a;
return b;
}
inline int read() {
int x = 0, f = 1;
char c = '\0';
while(c > '9' || c < '0') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
x = x * 10 + c - '0';
c = getchar();
}
return x * f;
}
struct node {
int to, nxt, w;
}edge[maxm << 1];
int head[maxn], cnt, n, m, tmpa, tmpb, tmpc;
int DFN[maxn], LOW[maxn], stack[maxn], top, tot, belong[maxn], sum;
bool vis[maxn];
void add(int u, int v, int w) {
edge[++cnt].to = v;
edge[cnt].nxt = head[u];
edge[cnt].w = w;
head[u] = cnt;
}
void Tarjan(int x) {
DFN[x] = LOW[x] = ++tot;
stack[++top] = x;
vis[x] = true;
for(register int i = head[x]; i; i = edge[i].nxt) {
int v = edge[i].to;
if(!DFN[v]) Tarjan(v), LOW[x] = min(LOW[x], LOW[v]);
if(vis[v])
LOW[x] = min(LOW[x], DFN[v]);
if(DFN[x] == LOW[x]) {
sum++;
do {
belong[stack[top]] = sum;
vis[stack[top]] = false;
top--;
}while(x != stack[top + 1]);
}
}
}
int main() {
n = read(); m = read();
for(register int i = 1; i <= m; ++i) {
tmpa = read(); tmpb = read(); tmpc = read();
add(tmpa, tmpb, tmpc);
add(tmpb, tmpa, tmpc);
}
for(register int i = 1; i <= n; ++i)
if(!DFN[i]) Tarjan(i);
return 0;
}
树链剖分
……
#include <iostream>
#include <cstdio>
#define maxn 1000005
using namespace std;
#define endl "\n"
inline int read() {
int x = 0, f = 1;
char c = '\0';
while(c > '9' || c < '0') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
x = x * 10 + c - '0';
c = getchar();
}
return x * f;
}
struct node {
int to, nxt;
}edge[maxn << 1];
int head[maxn], cnt, ct;
int t, p;
struct xx {
int sum, lc, rc, tag;
}a[maxn];
void add(int u, int v) {
edge[++cnt].to = v;
edge[cnt].nxt = head[u];
head[u] = cnt;
}
int n, m, res;
int f[maxn], depth[maxn], son[maxn], size[maxn], rk[maxn], top[maxn], id[maxn], w[maxn];
void Archmage(int x, int fa, int dp) {
depth[x] = dp;
size[x] = 1;
f[x] = fa;
for(register int i = head[x]; i; i = edge[i].nxt) {
int v = edge[i].to;
if(v == fa) continue;
Archmage(v, x, dp + 1);
size[x] += size[v];
if(size[v] > size[son[x]])
son[x] = v;
}
}
void Archmage_(int x, int t) {
top[x] = t;
id[x] = ++ct;
rk[ct] = w[x];
if(!son[x]) return;
Archmage_(son[x], t);
for(register int i = head[x]; i; i = edge[i].nxt) {
int v = edge[i].to;
if(v != son[x] && v != f[x])
Archmage_(v, v);
}
}
void pushup(int u) {
a[u].sum = (a[a[u].lc].sum + a[a[u].rc].sum) % p;
}
void pushdown(int u, int l, int r){
int mid = (l + r) >> 1;
a[a[u].lc].sum = (a[a[u].lc].sum + (mid - l + 1) * a[u].tag) % p;
a[a[u].lc].tag = (a[a[u].lc].tag + a[u].tag) % p;
a[a[u].rc].sum = (a[a[u].rc].sum + (r - mid) * a[u].tag) % p;
a[a[u].rc].tag = (a[a[u].rc].tag + a[u].tag) % p;
a[u].tag = 0;
}
void build(int u, int l, int r) {
if(l == r) {
a[u].sum = (rk[l]) % p;
return;
}
a[u].lc = ++t;
int mid = (l + r) >> 1;
build(a[u].lc, l, mid);
a[u].rc = ++t;
build(a[u].rc, mid + 1, r);
pushup(u);
}
void query(int u, int l, int r, int ll, int rr){
if(l == ll && r == rr){
res = (res + a[u].sum) % p;
return;
}
int mid = (l+r)/2;
pushdown(u,l,r);
if(rr <= mid) query(a[u].lc, l, mid, ll, rr);
else if(ll > mid) query(a[u].rc, mid+1, r, ll, rr);
else{
query(a[u].lc, l, mid, ll, mid);
query(a[u].rc, mid+1, r, mid+1, rr);
}
}
void update(int u, int l, int r, int ll, int rr, int w) {
if(l == ll && r == rr){
a[u].sum = (a[u].sum + (r - l + 1) * w) % p;
a[u].tag = (a[u].tag + w) % p;
return;
}
int mid = (l + r) >> 1;
pushdown(u,l,r);
if(ll > mid) update(a[u].rc, mid + 1, r, ll, rr, w);
else if(rr <= mid) update(a[u].lc, l, mid, ll, rr, w);
else{
update(a[u].lc, l, mid, ll, mid, w);
update(a[u].rc, mid + 1, r, mid + 1, rr, w);
}
pushup(u);
}
int qRange(int x, int y) {
int ans = 0;
while(top[x] != top[y]) {
if(depth[top[x]] < depth[top[y]])
swap(x, y);
res = 0;
query(1, 1, n, id[top[x]], id[x]);
ans = (ans + res) % p;
x = f[top[x]];
}
if(depth[x] > depth[y])
swap(x, y);
res = 0;
query(1, 1, n, id[x], id[y]);
ans = (ans + res) % p;
return ans;
}
void uRange(int x, int y, int k) {
while(top[x] != top[y]) {
if(depth[top[x]] < depth[top[y]])
swap(x, y);
update(1, 1, n, id[top[x]], id[x], k % p);
x = f[top[x]];
}
if(depth[x] > depth[y])
swap(x, y);
update(1, 1, n, id[x], id[y], k % p);
}
int qSon(int x) {
res = 0;
query(1, 1, n, id[x], id[x] + size[x] - 1);
return res % p;
}
void uSon(int x, int k) {
update(1, 1, n, id[x], id[x] + size[x] - 1, k % p);
}
int r;
void main() {
t = 1;
n = read(); m = read(); r = read(); p = read();
int tmpa, tmpb, tmpc, tmpd;
for(register int i = 1; i <= n; ++i)
w[i] = read();
for(register int i = 1; i < n; ++i) {
tmpa = read(); tmpb = read();
add(tmpa, tmpb);
add(tmpb, tmpa);
}
Archmage(r, 0, 1);
Archmage_(r, r);
build(1, 1, n);
for(register int i = 1; i <= m; ++i) {
tmpa = read();
if(tmpa == 1) {
tmpb = read(); tmpc = read(); tmpd = read();
uRange(tmpb, tmpc, tmpd);
}
else if(tmpa == 2) {
tmpb = read(); tmpc = read();
cout << (qRange(tmpb, tmpc)) % p << endl;
}
else if(tmpa == 3) {
tmpb = read(); tmpc = read();
uSon(tmpb, tmpc);
}
else {
tmpb = read();
cout << (qSon(tmpb)) % p << endl;
}
}
return 0;
}