from https://blog.csdn.net/weixin_40191952/article/details/89160419

打了这么久acm竞赛,也不过这些篇总结和一堆铜牌而已..

得到金牌的同学很优秀,可我们生活过的也是同样的时间,只不过我(我们)投入的少一些,走了一些弯路,做了更多其他的事。虽然写不进履历,但都如实构成了现在的我(我们)。

所以我觉得,那些不那么厉害的acmer也不必懊悔,感叹算法的神奇,经历过现场ac了以为超出自我水平的题目时的狂喜,有过全情投入的日子,即使不那么长,就足够了。

大学中我也接触过许多竞赛,也得过一些听起来比铜牌厉害的奖项。但只有acm,是我可以自豪的说,我打过acm,虽然成绩平平,但我喜欢它。如果本科再来一次,无论什么专业什么学校,我都希望还能在操场路邂逅acm海报,再次选择acm竞赛。

作为菜鸡把他们分享出来,不是想表示自己做了什么,而是将来如果有和我脑回路相似的初学者,能从我的经验里得到一些体会,少走一些弯路,就很棒了..

最后说一些模板本身的东西..最好是有这方面知识再去用模板,而不是像字典一样比赛带着就行。

模板本身都是自己写的,大半贴的不是一个类或函数,而是一整个代码文件,这是我的习惯..代码都测试过,但不保证100%正确(99%正确吧)。依稀记得并查集和C语言判断大数的好像有错过,但未必是模板错。

当然这个模板也是非常不全的..都是比较基础的..比我开始计划的要写的少很多..但也算是有些内容吧..

数论

O(nloglogn)的筛法:

for (i = 2; i*i <= mm; i++)

if (p[i] == 1)

for (j=i*i; j < mm; j += i)

p[j] = 0;

线性欧拉筛法 h[i]=0 where i is prime.p[i] is (i+1)th prime.z is number of prime under maxn。

for ( i = 2; i<maxn; i++)

{

if (!h[i])

p[z++] = i;

for (int j = 0; j<z; j++)

{

if (i*p[j]>maxn) break;

h[i*p[j]] = true;

if (i%p[j] == 0) break;

}

}

O(nlogn)欧拉函数

for (i = 1; i<maxnn; i++) a[i] = i;

for (i = 2; i<maxnn; i += 2) a[i] /= 2;

for (i = 3; i < maxnn; i += 2)

if (a[i] == i)

for (j = i; j < ; j += i)

a[j] = a[j] - a[j] / i;

米勒罗宾非确定算法判质数: O(slog³n)(1s判断万个longlong数,或用java自带函数:x=cin.nextBigInteger();if(x.isProbablePrime(1))...)

#include <bits/stdc++.h>

using namespace std;

#define LL long long

const int S = 8;

LL mult_mod(LL a, LL b, LL c)

{

a %= c, b %= c;

LL ret = 0, tmp = a;

while (b)

{

if (b & 1)

{

ret += tmp;

if (ret > c)

ret -= c;

}

tmp <<= 1;

if (tmp > c)

tmp -= c;

b >>= 1;

}

return ret;

}

LL pow_mod(LL a, LL n, LL mod)

{

LL ret = 1, temp = a % mod;

while (n)

{

if (n & 1)

ret = mult_mod(ret, temp, mod);

temp = mult_mod(temp, temp, mod);

n >>= 1;

}

return ret;

}

bool check(LL a, LL n, LL x, LL t)

{

LL ret = pow_mod(a, x, n), last = ret;

for (int i = 1; i <= t; i++)

{

ret = mult_mod(ret, ret, n);

if (ret == 1 && last != 1 && last != n - 1)

return 1;

last = ret;

}

if (ret != 1)

return 1;

return 0;

}

bool mill(LL n)

{

if (n < 2)

return 0;

if (n == 2)

return 1;

if ((n & 1) == 0)

return 0;

LL x = n - 1, t = 0;

while ((x & 1) == 0)

x >>= 1, t++;

srand(time(NULL));

for (int i = 0; i < S; i++)

{

LL a = rand() % (n - 1) + 1;

if (check(a, n, x, t))

return 0;

}

return 1;

}

int main()

{

LL n;

while (~scanf("%lld", &n))

puts(mill(n) ? "Yes" : "No");

}

java大数类可以用随机算法判断质数以及找下一个质数,判断质数的参数S是确定性,表示这个结果错误的概率为(1/2)^S,S与算法执行时间成正比(下一个质数函数默认参数S为100)

import java.math.*;

public class Main

{

public static void main(String[] args)

{

BigInteger bi1, bi2;

Boolean b1;

bi1 = new BigInteger("10633823966279326983230456482242756607");

bi2 = bi1.nextProbablePrime();

b1 = bi1.isProbablePrime(100);

String str1 = bi1+ " is prime with certainity  is " +b1;

System.out.println(str1);

System.out.println(bi2);

}

}

二分幂:

LL po(LL a, LL b)

{

LL ans = 1;

while (b)

{

if (b & 1)

ans = ans * a % mm;

a = a * a % mm;

b = b >> 1;

}

return ans;

}

逆元,求(a/b)%p时(p为质数),b^-1%p=po(b,p-2),即:

(a/b)%p=(a*po(b,p-2))%p

求逆元,除法时记得特判分母为0(比如等比数列公比为1)。

矩阵快速幂:
对于可以logn的求出递推式的第n项,将f(n)=xxx右边的所有项竖列,乘以传递矩阵得到f(n+1)=xxx右边的所有项竖列(一般右边有几项就是几阶矩阵)。比如:

#include <bits/stdc++.h>

using namespace std;

#define LL long long

#define mm 1000000007

#define nn 6

LL g[22];

struct mat { LL a[nn][nn]; };

mat mat_mul(mat x, mat y)

{

mat res;

memset(res.a, 0, sizeof(res.a));

for (int i = 0; i < nn; i++)

for (int j = 0; j < nn; j++)

for (int k = 0; k < nn; k++)

res.a[i][j] = (x.a[i][k] * y.a[k][j] + res.a[i][j]) % mm;

return res;

}

mat pow(mat a, LL n)

{

mat res;

memset(res.a, 0, sizeof(res.a));

for (int i = 0; i < nn; i++)

res.a[i][i] = 1;//单位矩阵

while (n)

{

if (n & 1)

res = mat_mul(res, a);

a = mat_mul(a, a);

n >>= 1;

}

return res;

}

int main()

{

//g[0] = 0, g[1] = 1;

//for (i = 2; i < 22; i++)

//g[i] = g[i - 2] + g[i - 1] + i * i*i + i * i + i + 1;

freopen("in.txt", "r", stdin);

freopen("out.txt", "w", stdout);

mat qq;

LL n, te[6][6] = {

1,1,1,1,1,1,

1,0,0,0,0,0,

0,0,1,3,3,1,

0,0,0,1,2,1,

0,0,0,0,1,1,

0,0,0,0,0,1 };

memcpy(qq.a, te, sizeof(te));

scanf("%*d");

while (~scanf("%lld", &n))

{

mat ans = pow(qq, n - 1);

LL viia = (ans.a[0][0] + 8 * ans.a[0][2] + 4 * ans.a[0][3] + 2 * ans.a[0][4] + ans.a[0][5]) % mm;

printf("%lld\n", viia);

}

return 0;

}

图论

并查集:

void init(int size)

{

for (i = 0; i < size; i++) u[i] = -1;

}

int find(int x)

{

if (u[x] < 0) return x;

u[x] = find(u[x]);

return u[x];

}

void mix(int x, int y)

{

if ((x = find(x)) == (y = find(y))) return;

if (u[x] < u[y])

u[x] += u[y], u[y] = x;

else

u[y] += u[x], u[x] = y;

}

拓扑排序(删除图中度小于2的点直到无点可删):

void top()

{

queue<int>q;

int i, te, v;

for (i = 1; i <= n; i++)

if (nu[i] < 2) vi[i] = 1, q.push(i);

while (!q.empty())

{

te = q.front(), q.pop();

for (i = h[te]; i; i = a[i].ne)

{

v = a[i].v;

if (vi[v] == 1) continue;

nu[v]--;

if (nu[v] < 2)

q.push(v), vi[v] = 1;

}

}

}

最小生成树:

void add(int u, int v, int w)//t初始为1

{

a[t].u = u, a[t].v = v, a[t++].w = w;

}

int cmp(viia x, viia y)

{

return x.w < y.w;

}

int find(int x)

{

if (!f[x]) return x;

f[x] = find(f[x]);

return f[x];

}

int kru()

{

sort(a + 1, a + t, cmp);

int i, ans = 0, w, u, v, t1, t2, cnt = 0;

for (i = 1; i < t; i++)

{

u = a[i].u, v = a[i].v, w = a[i].w;

t1 = find(u), t2 = find(v);

if (t1 != t2)

f[t1] = t2, ans += w, cnt++;

if (cnt == t - 1) return ans;

}

return -1;

}

最短路系列 都要链表存边(注意int溢出 初始化最大值足够大):

void add(int u, int v, int w)//初始k=1

{

a[k].v = v, a[k].w = w, a[k].ne = h[u], h[u] = k++;

}

队列优化spfa(适合稀疏图 总体比dij慢):

int spfa(int str, int end)

{

int i, u, v, w;

for (i = 1; i <= n; i++) d[i] = 1 << 28;

d[str] = 0;

memset(vi, 0, sizeof(vi));

queue<int>q;

q.push(str);

while (!q.empty())

{

u = q.front(), q.pop(), vi[u] = 0;

for (i = h[u]; i; i = a[i].ne)

{

v = a[i].v, w = a[i].w;

if (d[v] > d[u] + w)

{

d[v] = d[u] + w;

if (!vi[v])

vi[v] = 1, q.push(v);

}

}

}

return d[end];

}

栈优化spfa(玄学速度 有时最快有时最慢):

int spfa(int sta,int end)

{

int i, u, v, w, top = 0;

for (i = 1; i <= n; i++)  d[i] = 1 << 28;

d[sta] = 0, st[top++] = vi[sta] = 1;

while (top)

{

u = st[--top], vi[u] = 0;

for (i = h[u]; i; i = a[i].ne)

{

w = a[i].w, v = a[i].v;

if (d[v] > d[u] + w)

{

d[v] = d[u] + w;

if (!vi[v])

st[top++] = v, vi[v] = 1;

}

}

}

return d[end];

}

优先队列优化dij(适合稠密图 稳定速度 没见过卡这个tle的):

struct node

{

int v, c;

node(int _v = 0, int _c = 0) :v(_v), c(_c) {}

bool operator <(const node &r)const

{

return c > r.c;

}

};

int dij(int sta,int end)

{

int i, v, u, w, top = 0;

for (i = 1; i <= n; i++)

vi[i] = 0, d[i] = 1 << 28;

d[sta] = 0;

priority_queue<node>q;

q.push(node(sta, 0));

node tmp;

while (!q.empty())

{

tmp = q.top(), q.pop(), u = tmp.v;

if (vi[u])continue;

vi[u] = 1;

for (i = h[u]; i; i = a[i].ne)

{

v = a[i].v, w = a[i].w;

if (!vi[v] && d[v]>d[u] + w)

d[v] = d[u] + w, q.push(node(v, d[v]));

}

}

return d[end];

}

Lca最近公共祖先(tarjan离线算法 O(n+q)):

#include <bits/stdc++.h>

#define mm 40005

struct note

{

int u, v, w, lca, ne;

} edge[mm << 1], edge1[805];

int head[mm], ip, head1[mm], ip1, m, n, fa[mm], vis[mm], ance[mm], dir[mm];

//head&edge存单向边 head1&edge1存每组询问 以1点为根第i点深度为dir[i]

void init()

{

memset(vis, 0, sizeof(vis)), memset(dir, 0, sizeof(dir));

memset(head, -1, sizeof(head)), memset(head1, -1, sizeof(head1));

ip = ip1 = 0;

}

void add(int u, int v, int w)

{

edge[ip].v = v, edge[ip].w = w, edge[ip].ne = head[u], head[u] = ip++;

}

void add1(int u, int v)

{

edge1[ip1].u = u, edge1[ip1].v = v, edge1[ip1].lca = -1;

edge1[ip1].ne = head1[u], head1[u] = ip1++;

}

int  find(int x)

{

if (fa[x] == x)

return x;

return fa[x] = find(fa[x]);

}

void Union(int x, int y)

{

x = find(x), y = find(y);

if (x != y)

fa[y] = x;

}

void tarjan(int u)

{

int i, v, w;

vis[u] = 1, ance[u] = fa[u] = u;

for (i = head[u]; i != -1; i = edge[i].ne)

{

v = edge[i].v, w = edge[i].w;

if (!vis[v])

dir[v] = dir[u] + w, tarjan(v), Union(u, v);

}

for (i = head1[u]; i != -1; i = edge1[i].ne)

{

v = edge1[i].v;

if (vis[v])

edge1[i].lca = edge1[i ^ 1].lca = ance[find(v)];

}

}

int main()

{

//O(n+q)

int u, v, w, i, lca;

while (~scanf("%d%d", &n, &m))

{

init();

for (i = 1; i < n; i++)

scanf("%d%d%d", &u, &v, &w), add(u, v, w), add(v, u, w);

for (i = 0; i < m; i++)

scanf("%d%d", &u, &v), add1(u, v), add1(v, u);

dir[1] = 0, tarjan(1);

for (i = 0; i < m; i++)

{

u = edge1[i << 1].u, v = edge1[i << 1].v, lca = edge1[i << 1].lca;

printf("%d\n", dir[u] + dir[v] - 2 * dir[lca]);

}

}

}

例:dij求路程小于L的情况下第二种路最少走多少(dis维护答案 pb维护总路程和第二种路程 总路程小于L才更新)

#include <bits/stdc++.h>

using namespace std;

int mp[5005][5005], dis[5005], vi[5005], p[5005], b[5005];

int main()

{

int i, x, y, z, n, m1, m2, l, mi, ma;

while (~scanf("%d%d%d%d", &n, &m1, &m2, &l))

{

memset(mp, -1, sizeof(mp));

for (i = 0; i <= n; i++)

dis[i] = p[i] = 1 << 28, b[i] = vi[i] = 0;

for (i = 0; i < m1; i++)

scanf("%d%d", &x, &y), mp[x][y] = mp[y][x] = 0;

for (i = 0; i < m2; i++)

{

scanf("%d%d%d", &x, &y, &z);

if (mp[x][y] == -1 || mp[x][y] > z)

mp[x][y] = mp[y][x] = z;

}

dis[1] = p[1] = b[i] = 0;

while (1)

{

ma = 1 << 28, mi = -1;

for (i = 1; i <= n; i++)

if (vi[i] == 0 && ma > p[i])

mi = i, ma = p[i];

if (mi == -1)break;

vi[mi] = 1, dis[mi] = p[mi];

for (i = 1; i <= n; i++)

if (mp[mi][i] == 0)

p[i] = p[mi], b[i] = b[mi] + 1;

else if (mp[mi][i] != -1)

if (p[i] > p[mi] + mp[mi][i] && b[mi] + mp[mi][i] <= l)

p[i] = p[mi] + mp[mi][i], b[i] = b[mi] + mp[mi][i];

}

printf("%d\n", vi[n] ? dis[n] : -1);

}

}

二分图最大匹配

二分图:不存在偶数环的无向图(无环图必是二分图)(二分图并不一定连通)。

注意二分图的存图,map[i][j]表示左边第i个与右边第j个点有边(和通常的i->j的有向边不同),Vector或模拟链表也是同理。

二分图拆点后的最大匹配=原图最大匹配/2

最小点覆盖数:选取最少的点,使任意一条边至少有一个端点被选择

二分图的最小顶点覆盖=最大匹配数(或拆点后的最大匹配/2)

一般图的最小顶点覆盖=npc问题(一般图包括有向图,偶数环图)

最大独立点集:选取最多的点,使任意所选两点均不相连

二分图的最大独立点集=V-最大匹配数

一般图的最大独立点集=npc问题

最小路径覆盖:选择尽量少的路径覆盖所有点。(注意路径和边不是一个意思,路径是多条连续的边)

Dag的最小不相交路径覆盖=V-拆点后的最大匹配

Dag的最小可相交路径覆盖=V-先floyd再拆点的最大匹配

Dag的最大不可达集:选尽量多的点使其中任意2点都不可达

Dag的最大不可达集= Dag的可相交路径覆盖=V-先floyd再拆点的最大匹配

特殊的最大团用二分图解:

已知班级一些女孩和男孩,所有女生之间都相互认识,所有男生之间也相互认识,给出m对关系表示哪个女孩与哪个男孩认识。现在要选择一些学生来组成一个团,使得里面所有人都认识,求此团最大人数。

原图的最大团=补图的最大独立集,原图的最大独立集=补图的最大团

显然补图为二分图,(V-补图的最大匹配)即可。(补图就是把原图有的边去掉,没有的边加上)

O(VE)的dfs实现匈牙利算法:

#include <bits/stdc++.h>

using namespace std;

#define mm 205

int n, m, link[mm];

bool use[mm], mp[mm][mm];

//O(ve) 下标从1开始

bool dfs(int cap)

{

int i, j;

for (i = 1; i <= m; i++)

if (mp[cap][i] && !use[i])

{

use[i] = 1;

j = link[i];

link[i] = cap;

if (j == -1 || dfs(j))

return 1;

link[i] = j;

}

return 0;

}

int hungary()

{

int num = 0;

memset(link, -1, sizeof(link));

for (int i = 1; i <= n; i++)

{

memset(use, 0, sizeof(use));

if (dfs(i))

num++;

}

return num;

}

int main()

{

int i, num, tmp;

while (~scanf("%d%d", &n, &m))

{

memset(mp, 0, sizeof(mp));

for (i = 1; i <= n; i++)

{

scanf("%d", &num);

while (num-- && scanf("%d", &tmp))

mp[i][tmp] = 1;

}

printf("%d\n", hungary());

}

}

Vector加速版(甚至能过小几千的数据):

#include <bits/stdc++.h>

using namespace std;

vector<int>G[1505];

bool vis[1505];

int match[1505], n;

bool dfs(int u)

{

for (int i = 0; i < G[u].size(); i++)

{

int t = G[u][i];

if (!vis[t])

{

vis[t] = 1;

if (match[t] == -1 || dfs(match[t]))

{

match[t] = u;

return 1;

}

}

}

return 0;

}

int hungary()

{

int res = 0;

memset(match, -1, sizeof(match));

for (int i = 1; i <= n; i++)

{

memset(vis, 0, sizeof(vis));

if (dfs(i))

res++;

}

return res;

}

int main()

{

int i, num, tmp, m;

while (~scanf("%d%d", &n, &m))

{

for (i = 0; i < 1505; i++)

G[i].clear();

for (i = 1; i <= n; i++)

{

scanf("%d", &num);

while (num-- && scanf("%d", &tmp))

G[i].push_back(tmp);

}

printf("%d\n", hungary());

}

}

复杂度O(V^0.5E)的hopcroft_karp算法:(小数据下效率和上一个差不多,大数据时明显快点,很少有题目会卡这个算法..)

//hopcroft_karp算法,复杂度O(sqrt(n)*m)

#include <bits/stdc++.h>

using namespace std;

const int N = 320;

const int INF = 0x3f3f3f3f;

struct

{

int to, next;

} g[N * N];

int  head[N];

bool used[N];

int p, n;

int nx, ny, cnt, dis, dx[N], dy[N], cx[N], cy[N];

//nx,ny分别是左点集和右点集的点数

//dx,dy分别维护左点集和右点集的标号

//cx表示左点集中的点匹配的右点集中的点,cy正好相反

void add_edge(int v, int u)

{

g[cnt].to = u, g[cnt].next = head[v], head[v] = cnt++;

}

bool bfs() //寻找增广路径集,每次只寻找当前最短的增广路

{

queue<int> que;

dis = INF;

memset(dx, -1, sizeof dx);

memset(dy, -1, sizeof dy);

for (int i = 1; i <= nx; i++)

if (cx[i] == -1) //将未遍历的节点入队,并初始化次节点距离为0

que.push(i), dx[i] = 0;

while (!que.empty())

{

int v = que.front();

que.pop();

if (dx[v] > dis)

break;

for (int i = head[v]; i != -1; i = g[i].next)

{

int u = g[i].to;

if (dy[u] == -1)

{

dy[u] = dx[v] + 1;

if (cy[u] == -1)

dis = dy[u]; //找到了一条增广路,dis为增广路终点的标号

else

dx[cy[u]] = dy[u] + 1, que.push(cy[u]);

}

}

}

return dis != INF;

}

int dfs(int v)

{

for (int i = head[v]; i != -1; i = g[i].next)

{

int u = g[i].to;

if (!used[u] && dy[u] == dx[v] + 1)

//如果该点没有被遍历过并且距离为上一节点+1

{

used[u] = 1;

if (cy[u] != -1 && dy[u] == dis)

continue;

//u已被匹配且已到所有存在的增广路终点的标号,直接跳过

if (cy[u] == -1 || dfs(cy[u]))

{

cy[u] = v, cx[v] = u;

return 1;

}

}

}

return 0;

}

int hopcroft_karp()

{

int res = 0;

memset(cx, -1, sizeof cx);

memset(cy, -1, sizeof cy);

while (bfs())

{

memset(used, 0, sizeof used);

for (int i = 1; i <= nx; i++)

if (cx[i] == -1)

res += dfs(i);

}

return res;

}

int main()

{

int a, b;

while (~scanf("%d%d", &p, &n))

{

cnt = 0;

memset(head, -1, sizeof head);

for (int i = 1; i <= p; i++)

{

scanf("%d", &a);

for (int j = 0; j < a; j++)

{

scanf("%d", &b);

add_edge(i, b);

}

}

nx = p, ny = n;

printf("%d\n", hopcroft_karp());

}

}

带权二分图的匹配(最佳完美匹配)

如果二分图的每条边都有一个权(可以是负数),要求一种完备匹配方案,使得所有匹配边的权和最大,记做最佳完美匹配。(所以当所有边的权为1时,就是最大匹配)(最小匹配把所有边乘以-1就好)

Kuhn-Munkras算法,可以有负边,O(n^3)~O(n^4):

#include <bits/stdc++.h>

using namespace std;

#define maxn  305

#define INF  0x3f3f3f3f

int match[maxn], lx[maxn], ly[maxn], slack[maxn];

//lx,ly,slack存权值 match存表示最终第j个目标匹配到第i个上

//下标从0开始 大常数的O(n^3) 注意nx ny的赋值

//求最小匹配则乘以-1求最大

int n, nx, ny, ans, G[maxn][maxn];

bool visx[maxn], visy[maxn];

bool findpath(int x)

{

int tempDelta;

visx[x] = 1;

for (int y = 0; y < ny; ++y)

{

if (visy[y])

continue;

tempDelta = lx[x] + ly[y] - G[x][y];

if (tempDelta == 0)

{

visy[y] = 1;

if (match[y] == -1 || findpath(match[y]))

{

match[y] = x;

return 1;

}

}

else if (slack[y] > tempDelta)

slack[y] = tempDelta;

}

return 0;

}

void KM()

{

int x, i, j;

for (x = 0; x < nx; ++x)

{

for (j = 0; j < ny; ++j)

slack[j] = INF;

while (1)

{

memset(visx, 0, sizeof(visx));

memset(visy, 0, sizeof(visy));

if (findpath(x))

break;

else

{

int delta = INF;

for (j = 0; j < ny; ++j)

if (!visy[j] && delta > slack[j])

delta = slack[j];

for (i = 0; i < nx; ++i)

if (visx[i])

lx[i] -= delta;

for (j = 0; j < ny; ++j)

{

if (visy[j])

ly[j] += delta;

else

slack[j] -= delta;

}

}

}

}

}

void solve()

{

memset(match, -1, sizeof(match));

memset(ly, 0, sizeof(ly));

for (int i = 0; i < nx; ++i)

{

lx[i] = -INF;

for (int j = 0; j < ny; ++j)

if (lx[i] < G[i][j])

lx[i] = G[i][j];

}

KM();

}

int main()

{

int i, j, ans;

while (~scanf("%d", &n))

{

nx = ny = n;

for (i = 0; i < nx; ++i)

for (j = 0; j < ny; ++j)

scanf("%d", G[i] + j);

solve();

ans = 0;

for (i = 0; i < ny; ++i)

if (match[i] != -1)

ans += G[match[i]][i];

printf("%d\n", ans);

}

}

二分图的最小点权覆盖= 最大完美匹配(最大流)

二分图点权最大独立集=二分图点权和-二分图最小点权覆盖集

最大流:向图源点到汇点的最大流量。

矩阵存边的话重边需要加上

初始化最大值时要大于最终的最大流(比如所有边之和)

st2点间最大流=st两点间的最小割

O(m*n^2)的dinic算法:(poj 1273)

#include <bits/stdc++.h>

using namespace std;

#define inf 11111111

const int N = 255;

struct

{

int v, w, ne;

} ed[444];

int cnt, co, id[N], flor[N], cur[N];

//co为点数,遍历所有点时用到(函数中唯一需要改的地方)

void add(int a, int b, int x)

{

//加边一次加4个,直接调用就好

ed[cnt].v = b, ed[cnt].w = x;

ed[cnt].ne = id[a], id[a] = cnt++;

ed[cnt].v = a, ed[cnt].w = 0;

ed[cnt].ne = id[b], id[b] = cnt++;

}

int bfs(int s, int t)

{

queue<int> q;

memset(flor, 0, sizeof(flor));

flor[s] = 1, q.push(s);

while (q.size())

{

int now = q.front();

q.pop();

if (now == t)

return 1;

for (int i = id[now]; ~i; i = ed[i].ne)

{

int to = ed[i].v;

if (flor[to] == 0 && ed[i].w > 0)

{

flor[to] = flor[now] + 1;

q.push(to);

if (to == t)

return 1;

}

}

}

return flor[t] != 0;

}

int dfs(int s, int t, int value)

{

int ret = value, a;

if (s == t || value == 0)

return value;

for (int &i = cur[s]; ~i; i = ed[i].ne)

{

int to = ed[i].v;

if (flor[to] == flor[s] + 1 && (a = dfs(to, t, min(ret, ed[i].w))))

{

ed[i].w -= a, ed[i ^ 1].w += a;

ret -= a;

if (!ret)

break;

}

}

if (ret == value)

flor[s] = 0;

return value - ret;

}

int dinic(int s, int t)

{

int ret = 0;

while (bfs(s, t))

{

for (int i = 1; i <= co; i++)//遍历所有点,这里是需要修改的!!

cur[i] = id[i];

ret += dfs(s, t, inf);

}

return ret;

}

int main()

{

int a, b, c, n, m;

while (~scanf("%d%d", &m, &n))

{

memset(id, -1, sizeof(id)), cnt = 0;

for (int i = 0; i < m; i++)

{

scanf("%d%d%d", &a, &b, &c);

add(a, b, c);

}

co = n;

printf("%d\n", dinic(1, n));

}

return 0;

}

有上下界的网络流:就是有下界的最大流(上界本来就有)

流程是:

把每条边拆成必要边和不必要边(必要边是下界流量,不必要边是上界-下界)

加点x,y,将每个不必要边u->v拆成u->x和y->v,再加边原汇点->原源点,容量为无穷。此时新图为y是源点x是汇点的最大流图。

若最大流<所有流入x的边之和(即最大流小于流量下界和),则原图无解。

否则,记sum1为从s点流出的流量和。

在残余网络里,去掉st之间的所有边(就是上一步加的无穷边和反向0边这2个边),再做一次s到t的最大流(xy点存在与否并不影响,因为这2点只有出或入,并不能传递流量),最大流为sum2。

有下界的最大流流量即sum1 + sum2。

要求最大流的具体信息的话,第一次跑完存图为G1,第二次跑完图为G2,原图下界为LC,

则原图i->j流量为:G1[i][j] – G2[i][j] + LC[i][j]

有重边的话:注意流量和下界都要累加,合并为1条边。

下界不会是负数(mi = min(x, mi))。

在原最大流模板下加一个有下界的最大流函数,用二维矩阵存上下界,在函数内建图,跑2遍最大流记录sum1,sum2,并返回即可。

模板(上面和dinic一样,只多了一行数组定义):

#include <bits/stdc++.h>

using namespace std;

#define inf 11111111

const int N = 255;

struct

{

int v, w, ne;

} ed[444], ed1[444];

int cnt, co, id[N], flor[N], cur[N];

//co为点数,遍历所有点时用到(函数中唯一需要改的地方)

int mi[N][N], ma[N][N];

void add(int a, int b, int x)

{

//加边一次加4个,直接调用就好

ed[cnt].v = b, ed[cnt].w = x;

ed[cnt].ne = id[a], id[a] = cnt++;

ed[cnt].v = a, ed[cnt].w = 0;

ed[cnt].ne = id[b], id[b] = cnt++;

}

int bfs(int s, int t)

{

queue<int> q;

memset(flor, 0, sizeof(flor));

flor[s] = 1, q.push(s);

while (q.size())

{

int now = q.front();

q.pop();

if (now == t)

return 1;

for (int i = id[now]; ~i; i = ed[i].ne)

{

int to = ed[i].v;

if (flor[to] == 0 && ed[i].w > 0)

{

flor[to] = flor[now] + 1;

q.push(to);

if (to == t)

return 1;

}

}

}

return flor[t] != 0;

}

int dfs(int s, int t, int value)

{

int ret = value, a;

if (s == t || value == 0)

return value;

for (int &i = cur[s]; ~i; i = ed[i].ne)

{

int to = ed[i].v;

if (flor[to] == flor[s] + 1 && (a = dfs(to, t, min(ret, ed[i].w))))

{

ed[i].w -= a, ed[i ^ 1].w += a;

ret -= a;

if (!ret)

break;

}

}

if (ret == value)

flor[s] = 0;

return value - ret;

}

int dinic(int s, int t)

{

int ret = 0;

while (bfs(s, t))

{

for (int i = 1; i <= co; i++)//遍历所有点,这里是需要修改的!!

cur[i] = id[i];

ret += dfs(s, t, inf);

}

return ret;

}

int bound_flow(int s, int t)

{

//由mi ma数组求有上界的网络流 co为遍历所有点的参数,需要提前传入

memset(id, -1, sizeof(id));

int i, j, x = co + 1, y = co + 2;

int sum = 0, sum1 = 0, sum2 = 0, s1 = 0;

for (i = 0; i <= co; i++)

for (j = 0; j <= co; j++)

if (mi[i][j] != 0)//拆必要边

{

if (i == s)

s1 += mi[i][j];

add(i, x, mi[i][j]), add(y, j, mi[i][j]);

sum += mi[i][j];//必要边之和

}

for (i = 0; i <= co; i++)

for (j = 0; j <= co; j++)

if (ma[i][j] - mi[i][j] > 0)//加不必要边

{

if (i == s)

s1 += ma[i][j] - mi[i][j];

add(i, j, ma[i][j] - mi[i][j]);

}

add(t, s, inf);//加无穷边

memcpy(ed1, ed, sizeof(ed));

co = co + 2;//改点数

int temp = dinic(y, x);

if (temp != sum)

return -1;

i = s;

for (j = id[i]; ~j; j = ed[j].ne)

if (ed[j].w < ed1[j].w)

sum1 += ed1[j].w - ed[j].w;//计算sum1

for (j = id[i]; ~j; j = ed[j].ne)

if (ed[j].v == t)

ed[j].w = 0;

for (j = id[t]; ~j; j = ed[j].ne)

if (ed[j].v == s)

ed[j].w = 0;//删st边

sum2 = dinic(s, t);

return sum1 + sum2;

}

最小费用最大流:对于每条有向边i->j,都有一个单位费用和流量(流量每多1都要多一个单位费用),求s到t的最大流中,总费用最小的。

求无向图1->n->1的不重复最短路径。(以poj2135为例)

相当于求1->n 的2条不相交边的路径的和的最小。

建立源点汇点,源点连到1一条流量为2(表示2条路)费用为0的有向边,汇点同理连到n。

对于每条费用为c的无向边i<->j,,建立i->j和j->i的流量为1费用c的边(带0边)。

这时候源点到汇点的最小费用就是所求最短路。

#include <bits/stdc++.h>

using namespace std;

const int inf = 1 << 28, MAXN = 1005, MAXM = 10005 << 2;

struct

{

int s, to, ne, ca, va;//费用va 容量ca

} ed[MAXM];

int id[MAXN], pre[MAXN], dis[MAXN], cnt;

bool vis[MAXN];

void addedge(int a, int b, int v, int c)

{

//加边和反向0边

ed[cnt].to = b, ed[cnt].s = a, ed[cnt].va = v;

ed[cnt].ca = c, ed[cnt].ne = id[a], id[a] = cnt++;

ed[cnt].to = a, ed[cnt].s = b, ed[cnt].va = -v;

ed[cnt].ca = 0, ed[cnt].ne = id[b], id[b] = cnt++;

}

bool spfa(int s, int t, int nnum)

{

//[0,nnum]中s到t是否存在最短路

memset(vis, 0, sizeof(vis));

memset(pre, -1, sizeof(pre));

for (int i = 0; i <= nnum; i++)

dis[i] = inf;

queue<int> que;

que.push(s);

dis[s] = 0, vis[s] = 1;

while (!que.empty())

{

int temp = que.front();

que.pop(), vis[temp] = 0;

for (int i = id[temp]; ~i; i = ed[i].ne)

if (ed[i].ca)

{

int ne = ed[i].to;

if (dis[temp] + ed[i].va < dis[ne])

{

dis[ne] = dis[temp] + ed[i].va;

pre[ne] = i;

if (!vis[ne])

vis[ne] = 1, que.push(ne);

}

}

}

return dis[t] != inf;

}

int getMincost(int s, int t, int nnum)

{

//[0,nnum]中s到t的最小费用最大流的最小费用

int ans_flow = 0, ans_cost = 0, temp, minc;

while (spfa(s, t, nnum))

{

temp = t;

minc = inf;

while (pre[temp] != -1)

{

minc = min(ed[pre[temp]].ca, minc);

temp = ed[pre[temp]].s;

}

temp = t;

while (pre[temp] != -1)

{

ed[pre[temp]].ca -= minc;

int ss = pre[temp] ^ 1;

ed[ss].ca += minc;

temp = ed[pre[temp]].s;

}

ans_cost += dis[t] * minc;

}

return ans_cost;

}

int main()

{

int i, a, b, v, s, t, n, m;

while (~scanf("%d%d", &n, &m))

{

memset(id, -1, sizeof(id)), cnt = 0;

memset(ed, 0, sizeof(ed));

for (i = 0; i < m; i++)

{

scanf("%d%d%d", &a, &b, &v);

addedge(a, b, v, 1);

addedge(b, a, v, 1);

}

s = n + 1, t = n + 2;

addedge(s, 1, 0, 2);

addedge(n, t, 0, 2);

printf("%d\n", getMincost(s, t, t));

}

return 0;

}

连通性:

强连通:有向图中,如果任意2点都相互可达,则该图是强连通图。

强连通分量:有向图中,其强连通图子图,称为强连通分量。(缩点后每个点都原图中最大的强连通分量)

一个有向图是强连通的,等价于G中有一个回路,它至少包含每个节点一次。(只是一笔画经过所有点回到原点,点可以通过多次,不一定是一个大环,也可能是几个小环的拼接。但环上的所有点一定构成强连通分量)。

一些问题只要变成有向无环图就容易解决,但其中有环就比较难办,而环等价于强连通分量,把每个强连通分量缩成一个点,就是dag了。

常用算法是tarjan算法,复杂度是O(n+m),线性的。(注意有很多个tarjan算法..这个是求强连通的,还有离线求lca的,求双联通的..不是同一个算法...)

一些简单推论:

从任一点出发都可以到达的点有几个?

缩点后如果出度为0的点集唯一,符合条件的点就是该点集中的点,否则不存在符合条件的点。(poj 2186)

最小点基:选择最少的点,使得从这些点出发可以到达所有点。

最小权点基:选择权和尽量小的点集,使得从这些点出发可以到达所有点。(hdu5934)

解法:入度为0的强连通分量个数即为最小点基,从每个入度为0的强连通分量中取出权值最小的点,构成的集合即最小权点基。

最少加多少边能使图变为强连通图?(poj1236)

Ans = max(缩点后入度为0的点集,缩点后出度为0的点集)(特判如果缩点后只有一个点则原图已经强连通,ans = 0)

以poj1236为例(求最小点基点数,及至少加几个点变成强连通图),比较模板的写法(主函数中只需要调用,缩点后的信息都有直接处理好,加边后跑一遍当做黑盒用就好..)

#include <bits/stdc++.h>

#define ll long long

#define mm 10005

using namespace std;

stack<int> sta;

bool vis[mm], in[mm], out[mm];

//vis点是否在栈中 缩点后的点是否有出度入度

int n, m, tim, num, cnt;

//tim点的标记 num强连通分量个数(缩点后的点数) cnt链表计数 都是从1开始

int h[mm], dfn[mm], low[mm], siz[mm], bel[mm];

//bel:u是属于哪个集合中的 siz[i]第i个强连通的点数

int xx[mm], yy[mm];//备份边

struct

{

int to, ne;

} ed[mm * 5];

void init()

{

memset(vis, 0, sizeof(vis)), memset(in, 0, sizeof(in));

memset(h, 0, sizeof(h)), memset(dfn, 0, sizeof(dfn));

memset(low, 0, sizeof(low)), memset(bel, 0, sizeof(bel));

memset(siz, 0, sizeof(siz)), memset(out, 0, sizeof(out));

cnt = tim = num = 0;

while (!sta.empty())

sta.pop();

}

void add(int u, int v)

{

ed[++cnt].to = v, ed[cnt].ne = h[u], h[u] = cnt;

xx[cnt] = u, yy[cnt] = v;

}

void tarjan(int u)

{

dfn[u] = low[u] = ++tim;

vis[u] = 1;

sta.push(u);

for (int i = h[u]; i; i = ed[i].ne)

{

int v = ed[i].to;

if (!dfn[v])

tarjan(v), low[u] = min(low[u], low[v]);

else if (vis[v])

low[u] = min(low[u], dfn[v]);

}

if (dfn[u] == low[u])//发现新的强连通分量

{

int v;

num++;

do

{

v = sta.top(), sta.pop();

vis[v] = 0, bel[v] = num;

siz[num]++;//num从1开始

} while (u != v);

}

}

int main()

{

int i, j, te;

while (~scanf("%d", &n))

{

init();

for (i = 1; i <= n; i++)

while (scanf("%d", &te) && te)

add(i, te);

for (i = 1; i <= n; i++)

if (!dfn[i])

tarjan(i);

//缩点后共num个点 i缩点后为bel[i] 缩点后第x个点中有原siz[x]个点

for (i = 1; i <= cnt; i++)

if (bel[xx[i]] != bel[yy[i]])

out[bel[xx[i]]] = 1, in[bel[yy[i]]] = 1;

//点集bel[x[i]]有出度 bel[y[i]]有入度

if (num == 1)

{

puts("1\n0");

continue;

}

int ans1 = 0, ans2 = 0;

for (i = 1; i <= num; i++)

{

if (in[i] == 0)

ans1++;

if (out[i] == 0)

ans2++;

}

printf("%d\n%d\n", ans1, max(ans1, ans2));

}

return 0;

}

数据结构

单点修改线段树:(单点增加+区间和查询为例)

#include<bits/stdc++.h>

using namespace std;

struct { int l, r, n; }t[50005 << 2];

void build(int l, int r, int i)

{

t[i].l = l, t[i].r = r, t[i].n = 0;

if (l == r)

return;

int mid = (l + r) >> 1;

build(l, mid, i << 1), build(mid + 1, r, i << 1 | 1);

}

void update(int i, int x, int k)

{

t[k].n += x;

if (t[k].l == t[k].r)

return;

int mid = (t[k].l + t[k].r) >> 1;

if (i <= mid)

update(i, x, k << 1);

else

update(i, x, k << 1 | 1);

}

int sea(int l, int r, int k)

{

if (t[k].l == l && t[k].r == r)

return t[k].n;

int mid = (t[k].r + t[k].l) >> 1;

if (r <= mid)

return sea(l, r, k << 1);

if (l > mid)

return sea(l, r, k << 1 | 1);

return sea(l, mid, k << 1) + sea(mid + 1, r, k << 1 | 1);

}

int main()

{

char ss[13];

int tt, i, n, x, y;

scanf("%d", &tt);

for (int ca = 1; ca <= tt; ca++)

{

printf("Case %d:\n", ca);

scanf("%d", &n);

build(1, n, 1);

for (i = 1; i <= n; i++)

scanf("%d", &x), update(i, x, 1);

while (scanf("%s", ss) && ss[0] != 'E')

{

scanf("%d%d", &x, &y);

if (ss[0] == 'Q')

printf("%d\n", sea(x, y, 1));

if (ss[0] == 'A')

update(x, y, 1);

if (ss[0] == 'S')

update(x, -y, 1);

}

}

return 0;

}

区间修改线段树(区间增加+查询为例):

#include <bits/stdc++.h>

using namespace std;

#define LL long long

struct { int l, r; LL n, ad; } t[100005 << 2];

void fx(int k, LL c)//i点的值和标记都更新c

{

t[k].n += (t[k].r - t[k].l + 1)*c;

t[k].ad += c;

}

void up(int k)//根据子节点更新该点

{

t[k].n = t[k << 1].n + t[k << 1 | 1].n;

}

void down(int k)//将lazy标记推向子节点

{

fx(k << 1, t[k].ad), fx(k << 1 | 1, t[k].ad);

t[k].ad = 0;

}

void build(int l, int r, int k)

{

t[k].l = l, t[k].r = r;

t[k].n = t[k].ad = 0;

if (l == r)

return;

int mid = (l + r) >> 1;

build(l, mid, k << 1), build(mid + 1, r, k << 1 | 1);

}

void update(int l, int r, int c, int k)

{

if (l == t[k].l && r == t[k].r)

{

fx(k, c);

return;

}

if (t[k].ad)

down(k);

int mid = (t[k].l + t[k].r) >> 1;

if (r <= mid)

update(l, r, c, k << 1);

else if (l > mid)

update(l, r, c, k << 1 | 1);

else update(l, mid, c, k << 1), update(mid + 1, r, c, k << 1 | 1);

up(k);

}

LL sea(int l, int r, int k)

{

if (l == t[k].l && r == t[k].r)

return t[k].n;

if (t[k].ad)

down(k);

int mid = (t[k].r + t[k].l) >> 1;

if (r <= mid)

return sea(l, r, k << 1);

if (l > mid)

return sea(l, r, k << 1 | 1);

return sea(l, mid, k << 1) + sea(mid + 1, r, k << 1 | 1);

up(k);

}

int main()

{

int i, n, q, l, r, c;

LL x, y;

char s[33];

while (~scanf("%d%d", &n, &q))

{

build(1, n, 1);

for (i = 0; i < n; i++)

{

scanf("%lld", &x);

update(i + 1, i + 1, x, 1);

}

while (q-- && scanf("%s", s))

{

if (s[0] == 'Q')

{

scanf("%d%d", &l, &r);

printf("%lld\n", sea(l, r, 1));

}

if (s[0] == 'C')

{

scanf("%d%d%d", &l, &r, &c);

update(l, r, c, 1);

}

}

}

return 0;

}

线段树扫描线求面积交O(nlogn):

#include <bits/stdc++.h>

using namespace std;

struct

{

int l, r, co;

double n1, n2;

} t[2005 << 2];

struct lin

{

double x, y1, y2;

int f;

} sc[2005];

double y[2005];

int cmp(lin a, lin b)

{

return a.x < b.x;

}

void build(int l, int r, int i)

{

t[i].l = l, t[i].r = r, t[i].co = 0, t[i].n1 = t[i].n2 = 0;

if (r - l == 1)

return;

int mid = (l + r) >> 1;

build(l, mid, i << 1), build(mid, r, i << 1 | 1);

}

void up(int i)

{

if (t[i].co > 1)

t[i].n2 = y[t[i].r] - y[t[i].l], t[i].n1 = 0;

else if (t[i].co == 1)

if (t[i].r - t[i].l != 1)

{

t[i].n2 = t[i << 1].n1 + t[i << 1].n2 +

t[i << 1 | 1].n1 + t[i << 1 | 1].n2;

t[i].n1 = y[t[i].r] - y[t[i].l] - t[i].n2;

}

else

{

t[i].n2 = 0;

t[i].n1 = y[t[i].r] - y[t[i].l];

}

else if (t[i].r - t[i].l == 1)

&

repost 懊悔自己没能在本科和研究生期间邂逅ACM相关推荐

  1. 超六成专科生的月薪不足5000元?网友:本科和研究生也是

    前段时间,第一财经日报报道了一个调查,63.4%受访专科毕业生月薪低于5000元,月薪过万的受访专科毕业生更是不足5%. △截图来源微博,如侵删 不少网友表示:很多本科生月薪有5000元也很不错了,还 ...

  2. 阿德莱德大学计算机专业学费,阿德莱德大学本科及研究生学费是多少(含各专业学费)?...

    原标题:阿德莱德大学本科及研究生学费是多少(含各专业学费)? [加学姐微信liuxue1609,拉你进500人留学申请答疑群] 本文来自墨尔本大学留学回国的顾问老师CECY,如果大家想继续了解澳洲留学 ...

  3. 地信遥感专业(本科、研究生小白适用)的网站资源分享(持续更新)

    地信遥感专业(本科.研究生小白适用)的网站资源分享(持续更新) 本文章主要根据自己的学习,科研经历所写,只是冰山一角,希望对你有所帮助. 目录 微信公众号(知名院校或相关单位创建) 学术期刊 软件下载 ...

  4. 连续两个南航的研究生面试出了从来没出现过的问题,本科和研究生都是计算机专业的,竟然说static是不可更改的。

    最近面试人数有点多,面试有点频繁,因此发现了一些学生普遍会发生的错误,可以说是很离谱. 因为做了十多年的面试官,无论是大中小厂的面试,还是社招.校招. 从来没有遇到过这样的情况,而且发生在两个南航研究 ...

  5. 清华博士找工作7个月没着落,本科学历影响几时休?

    关注"视学算法",一起学习一起冲鸭! 设为星标,第一时间获取更多干货 作者:胡懋仁 来源:胡懋仁科学网博客 来自:募格学术.深度学习工坊 编辑:王萌(深度学习冲鸭公众号) 著作权归 ...

  6. 纽约大学计算机科学研究生录取条件,纽约大学本科及研究生录取条件 申请纽约大学干货分享...

    纽约大学既是美国常春藤盟校之一,也是美国大学协会的成员之一,这一大学在世界范围内都享有盛誉.每年计划申请这一大学的国际学生不计其数,纽约大学的录取条件都有哪些呢?请看出国留学网小编下文整理. 纽约大学 ...

  7. 11年程序员给本科、研究生应届生以及准备从事后台开发同学的建议,学习进阶之路

    校招形势 目前校招招聘中,后台开发或服务器开发的岗位需求一度火热,甚至超过了算法岗.不少同学从诸神黄昏的算法岗战场上退下,转向更偏向工程能力的后台开发岗,从而造成后台开发岗位竞争的大爆发. 后台开发工 ...

  8. 华科计算机学院新生家长群是多少,关于开通2020级本科和研究生新生群及新生家长群的公告...

    各位新生家长及2020级新同学: 为了帮助2020级新生了解适应新环境.明确学习目标.端正学习态度.合理规划大学生活,学院现将2020级新生群号及新生家长群号公布如下,请大家实名添加,以便及时为大家答 ...

  9. 【工作相关】公子龙:工作后我变强了,暂时没秃

    工作后,职场新人通常没法挑肥拣瘦,我觉得这不是一件坏事. 1. 以前读书的时候,参加一些大型专业比赛,经常会和一些工作了的人组队,久而久之成了关系不错的朋友. 当时,我很羡慕这些新认识的小伙伴,除了他 ...

最新文章

  1. hadoop、spark/storm等大数据相关视频资料汇总下载
  2. 笔记-高项案例题-2019年下-计算题
  3. VoIP安全问题解析
  4. Zookeeper Learning
  5. Codeforces Round #655 (Div. 2) B. Omkar and Last Class of Math 数学
  6. pandas.Series.rank用法详解
  7. A Dicey Problem 骰子难题(Uva 810)
  8. python大神的程序_6年Python大神总结10个开发技巧,80%的人都不会
  9. Linux Linux程序练习十(网络编程大文件发送)
  10. 集合之Collection接口AND Iterator迭代器 AND 增强for AND 泛型
  11. 2.数据可视化 Echarts项目 旋转地球
  12. 毕业设计——阶段性成果展示
  13. 拿捏住C字符串,这个烦人程度不亚于指针的小东西
  14. /opt/hbase/conf 中不能启动hbase_北京现代伊兰特不能启动
  15. SmartZoneOCR识别控件免费下载地址
  16. ehviewer_EhViewer官方下载_EhViewer最新版app下载 安卓版v1.7.1 - 浪浪下载
  17. dnf加物理攻击的卡片有哪些_dnf加物理攻击力的宝珠有哪些 dnf物攻宝珠
  18. kali linux添加软件源
  19. request.POST.get
  20. 解决海信智能电视不能安装U盘上的安装包的问题

热门文章

  1. 12.22 @烤仔建工 | 烤仔家具城建设中……
  2. 习题 8.21 用指向指针的指针的方法对n个整数排序并输出。要求将排序单独写成一个函数。n个整数在主函数中输入,最后在主函数中输出。
  3. 六个方面对360网站卫士与百度加速乐的比较
  4. “打怪升级”,电竞浪潮中一家非典型公司的生存之道
  5. 山东大学nlp实验--词向量
  6. nagios-邮件报警
  7. 微分方程的特征值解法:斯图姆-刘维尔方程
  8. 3DSMax怎么设置摄像机参数_3DSMax设置摄像机参数 小白教程
  9. 黑苹果OC引导AX201网卡教程,小新Pro13不换网卡也能上网
  10. 春游4.1 | 湖南多校赛20190331 / 16东京区域赛