@Dmaxiya
2020-04-13T12:05:24.000000Z
字数 9433
阅读 1302
板子
const int maxn = 5000 + 100;
// 欧拉路径输出从cnt – 1 到0输出u点下标,最后输出v点下标
struct Node {
int to;
int Index;
Node() {}
Node(int t, int i) {
to = t;
Index = i;
}
};
vector<Node> G[maxn];
int deg[maxn];
bool visit[maxn];
int n, m, v, u;
struct Edge {
int u, v;
Edge() {}
Edge(int uu, int vv) {
u = uu;
v = vv;
}
};
Edge e[maxn];
int cnt;
void dfs(int x) {
int len = G[x].size();
for(int i = 0; i < len; ++i) {
int Index = G[x][i].Index;
int pos = G[x][i].to;
if(!visit[Index]) {
visit[Index] = true;
dfs(pos);
e[cnt++] = Edge(x, pos);
}
}
}
for(int i = 0; i <= n; ++i) {
if(deg[i] % 2 == 1) {
u = i;
break;
}
}
dfs(u);
for(int i = cnt - 1; i >= 0; --i) {
printf("%d ", e[i].u);
}
printf("%d\n", e[0].v);
const int maxn = 1000 + 100;
int top, bcnt, dcnt, n;
int sta[maxn], dfn[maxn], low[maxn], belong[maxn];
bool ins[maxn];
vector<int> G[maxn];
vector<int> GG[maxn];
// G 为原图,GG 为缩点后的图
// 缩点后的图总共有 bcnt 个点,下标范围为 [1, bcnt]
// 若为无向图,dfs 时只需要多记录一个父节点
// 使用时构好图到 G 中,直接调用 Tarjan 函数
// belong[a] = b 表示原图上的节点 a 被缩至新图上的节点 b
void dfs(int x) {
dfn[x] = low[x] = ++dcnt;
ins[x] = true;
sta[top++] = x;
int len = G[x].size();
for(int i = 0; i < len; ++i) {
int pos = G[x][i];
if(dfn[pos] == 0) {
dfs(pos);
low[x] = min(low[x], low[pos]);
} else if(ins[pos]) {
low[x] = min(low[x], dfn[pos]);
}
}
if(dfn[x] == low[x]) {
++bcnt;
int pos;
do {
pos = sta[--top];
ins[pos] = false;
belong[pos] = bcnt;
} while(pos != x);
}
}
void Tarjan() {
bcnt = top = dcnt = 0;
memset(dfn + 1, 0, sizeof(int) * n);
memset(ins + 1, 0, sizeof(bool) * n);
for(int i = 1; i <= n; ++i) {
if(dfn[i] == 0) {
dfs(i);
}
}
for(int i = 1; i <= bcnt; ++i) {
GG[i].clear();
}
for(int i = 1; i <= n; ++i) {
int len = G[i].size();
for(int j = 0; j < len; ++j) {
int pos = G[i][j];
int u = belong[i];
int v = belong[pos];
if(u != v) {
GG[u].push_back(v);
}
}
}
}
const int Log = 20;
const int maxn = 100000 + 100;
int n, id_cnt;
int fa[Log][maxn];
vector<int> G[maxn];
int id[maxn], depth[maxn];
// 0 为树的虚根,depth[0] = 1,id[0] = 0
// Log 等于树的层数取 log 的值,n 为树上点的数量,点下标从 1 开始
void dfs(int f, int x, int d) {
depth[x] = d;
fa[0][x] = f;
id[x] = ++id_cnt;
int len = G[x].size();
for(int i = 0; i < len; ++i) {
int pos = G[x][i];
if(pos != f) {
dfs(x, pos, d + 1);
}
}
}
void prepare_skip_table() {
id_cnt = 0;
dfs(0, 1, 1);
for(int j = 1; j < Log; ++j) {
for(int i = 1; i <= n; ++i) {
fa[j][i] = fa[j - 1][fa[j - 1][i]];
}
}
}
int Find(int x, int y) {
if(x == y) {
return x;
}
if(id[x] > id[y]) {
swap(x, y);
}
for(int i = Log - 1; i >= 0; --i) {
if(id[fa[i][y]] > id[x]) {
y = fa[i][y];
}
}
return fa[0][y];
}
const int maxn = 100 + 100;
int dis[maxn];
vector<Node> G[maxn];
priority_queue<Node> que;
// 优先队列为小顶堆,排序关键字为Node.dis,形参s 为起点下标
void dij(int s) {
memset(dis, 0x3f, sizeof(dis));
dis[s] = 0;
que.push(Node(s, 0));
while(!que.empty()) {
Node tmp = que.top();
que.pop();
int len = G[tmp.pos].size();
for(int i = 0; i < len; ++i) {
int pos = G[tmp.pos][i].pos;
int d = G[tmp.pos][i].dis;
if(dis[pos] > tmp.dis + d) {
dis[pos] = tmp.dis + d;
que.push(Node(pos, dis[pos]));
}
}
}
}
const int maxn = 100000 + 100;
vector<Node> G[maxn];
priority_queue<Node> que;
int dis1[maxn], dis2[maxn];
// 优先队列为小顶堆,排序关键字为Node.dis,形参s 为起点下标
// dis1 为最短路,dis2 为次短路
void dij(int s) {
memset(dis1, 0x3f, sizeof(dis1));
memset(dis2, 0x3f, sizeof(dis2));
dis1[s] = 0;
que.push(Node(s, 0));
while(!que.empty()) {
Node tmp = que.top();
que.pop();
int pos = tmp.pos;
int dis = tmp.dis;
if(dis2[pos] < dis) {
continue;
}
int len = G[pos].size();
for(int i = 0; i < len; ++i) {
tmp = G[pos][i];
int to = tmp.pos;
int d2 = dis + tmp.dis;
if(dis1[to] > d2) {
swap(d2, dis1[to]);
que.push(Node(to, dis1[to]));
}
if(dis2[to] > d2 && dis1[to] < d2) {
dis2[to] = d2;
que.push(Node(to, dis2[to]));
}
}
}
}
const int maxn = 1000 + 100;
const int INF = 0x3f3f3f3f;
struct Node {
int pos, dis;
Node() {}
Node(int p, int d) {
pos = p;
dis = d;
}
};
int incnt[maxn], dis[maxn];
bool inque[maxn];
vector<Node> G[maxn];
// maxn 为节点个数,下标从 1 开始
// incnt 表示第 i 个节点入队的次数
// dis 表示以 s 为起点的最短路
// spfa 返回 false 表示有负环
bool spfa(int s, int n) {
memset(inque + 1, 0, sizeof(bool) * n);
memset(dis + 1, 0x3f, sizeof(int) * n);
memset(incnt + 1, 0, sizeof(int) * n);
dis[s] = 0;
inque[s] = true;
incnt[s] = 1;
queue<int> que;
que.push(s);
while(!que.empty()) {
int tmp = que.front();
que.pop();
inque[tmp] = false;
int len = G[tmp].size();
for(int i = 0; i < len; ++i) {
int pos = G[tmp][i].pos;
int d = G[tmp][i].dis;
if(dis[pos] > dis[tmp] + d) {
dis[pos] = dis[tmp] + d;
if(!inque[pos]) {
inque[pos] = true;
que.push(pos);
if(++incnt[pos] > n) {
return false;
}
}
}
}
}
return true;
}
const int maxn = 1000 + 100;
int n;
int pre[maxn];
int ddis[maxn];
int dis[maxn][maxn];
bool visit[maxn];
queue<int> que;
vector<int> G[maxn];
// n 为图上节点数量,pre表示节点i与生成树距离最短连边的节点
// ddis表示节点i到最小生成树的距离,dis为完全图上任意两点间距离
// visit表示是否在最小生成树上,G为最小生成树
void Prim() {
memset(visit, 0, sizeof(visit));
memset(ddis, 0x3f, sizeof(ddis));
for(int i = 1; i <= n; ++i) {
G[i].clear();
}
que.push(1);
while(!que.empty()) {
int tmp = que.front();
que.pop();
ddis[tmp] = 0;
visit[tmp] = true;
int Min = INT_MAX;
int pos = -1;
for(int i = 1; i <= n; ++i) {
if(!visit[i]) {
if(ddis[i] > dis[tmp][i]) {
ddis[i] = dis[tmp][i];
pre[i] = tmp;
}
}
}
for(int i = 1; i <= n; ++i) {
if(!visit[i] && Min > ddis[i]) {
Min = ddis[i];
pos = i;
}
}
if(pos != -1) {
tmp = pre[pos];
que.push(pos);
G[tmp].push_back(pos);
G[pos].push_back(tmp);
}
}
}
const int maxn = 1000 + 100;
int fa[maxn];
// 节点下标范围为[from, to]
inline void init(int from, int to) {
For(i, from, to) fa[i] = i;
}
int Find(int x) {
return ((x == fa[x])? x: (fa[x] = Find(fa[x])));
}
void unit(int x, int y) {
int xx = Find(x);
int yy = Find(y);
fa[xx] = yy;
}
const int maxn = 3001;
int Nx, Ny;
int dx[maxn], dy[maxn];
int Mx[maxn], My[maxn];
vector<int> G[maxn];
bool vis[maxn];
// 使用时需设好 Nx Ny 和 G,G 中只需要存下从 Nx 到 Ny 所有节点的边,不需要从 Ny 到 Nx 的边
// 时间复杂度 O(sqrt(n)*m),n 为节点数,m为边数
// Nx, Ny 分别表示左右两边节点数量,编号从 1 开始
// 最终匹配结果存在 Mx 与 My 中
// Mx[a] = b 表示左边的 a 与右边的 b 匹配,同时 My[b] = a
// 直接调用 hopcroft_karp 函数返回最大匹配数量
bool Find(int x) {
int len = G[x].size();
for(int i = 0; i < len; ++i) {
int pos = G[x][i];
if(!vis[pos] && dy[pos] == dx[x] + 1) {
vis[pos] = true;
if(My[pos] == 0 || Find(My[pos])) {
My[pos] = x;
Mx[x] = pos;
return true;
}
}
}
return false;
}
int hopcroft_karp() {
int ret = 0;
memset(Mx + 1, 0, sizeof(int) * Nx);
memset(My + 1, 0, sizeof(int) * Ny);
bool flag = true;
while(flag) {
flag = false;
queue<int> que;
for(int i = 1; i <= Nx; ++i) {
if(Mx[i] == 0) {
que.push(i);
}
}
memset(dx + 1, 0, sizeof(int) * Nx);
memset(dy + 1, 0, sizeof(int) * Ny);
memset(vis + 1, 0, sizeof(bool) * Ny);
while(!que.empty()) {
int x = que.front();
que.pop();
int len = G[x].size();
for(int i = 0; i < len; ++i) {
int pos = G[x][i];
if(dy[pos] == 0) {
dy[pos] = dx[x] + 1;
if(My[pos] != 0) {
dx[My[pos]] = dy[pos] + 1;
que.push(My[pos]);
} else {
flag = true;
}
}
}
}
if(!flag) {
break;
}
for(int i = 1; i <= Nx; ++i) {
if(!Mx[i] && Find(i)) {
++ret;
}
}
}
return ret;
}
const int maxn = 301;
const LL INF = 1e18;
int n;
LL G[maxn][maxn];
LL lx[maxn], ly[maxn], slack[maxn];
int My[maxn], Mx[maxn];
int pre[maxn];
bool visy[maxn];
// 使用时需设好 n 和 G,下标从 1 开始
// 时间复杂度 O(n^3),n 为节点数
// 最终匹配结果存在 Mx 与 My 中
// Mx[a] = b 表示左边的 a 与右边的 b 匹配,同时 My[b] = a
// 直接调用 KM 函数返回最大匹配权值
// 求最小权匹配可将权值取相反数
void augment(int root) {
fill(visy + 1, visy + n + 1, false);
fill(slack + 1, slack + n + 1, INF);
int py;
My[py = 0] = root;
do {
visy[py] = true;
int x = My[py], yy;
LL delta = INF;
for(int y = 1; y <= n; y++) {
if(!visy[y]) {
if(lx[x] + ly[y] - G[x][y] < slack[y]) {
slack[y] = lx[x] + ly[y] - G[x][y];
pre[y] = py;
}
if(slack[y] < delta) {
delta = slack[y];
yy = y;
}
}
}
for(int y = 0; y <= n; y++) {
if(visy[y]) {
lx[My[y]] -= delta;
ly[y] += delta;
} else {
slack[y] -= delta;
}
}
py = yy;
} while(My[py] != -1);
do {
int Pre = pre[py];
My[py] = My[Pre];
py = Pre;
} while(py != 0);
}
LL KM() {
for(int i = 1; i <= n; i++) {
lx[i] = ly[i] = 0;
My[i] = -1;
for(int j = 1; j <= n; j++) {
lx[i] = max(lx[i], G[i][j]);
}
}
LL answer = 0;
for(int root = 1; root <= n; root++) {
augment(root);
}
for(int i = 1; i <= n; ++i) {
Mx[My[i]] = i;
}
for(int i = 1; i <= n; i++) {
answer += lx[i];
answer += ly[i];
}
return answer;
}
const int maxn = 200 + 100;
const int maxm = 200 + 100;
const int INF = 0x3f3f3f3f;
// 时间复杂度为 O(n^2 * m),n 为节点数量,m 为边数
// maxm 为需要加边的数量,即调用 addEdge 的总次数
// 使用前需调用 Init 初始化
struct Edge {
int to, Next, flow, cap;
Edge() {}
Edge(int t, int n, int f, int c) {
to = t;
Next = n;
flow = f;
cap = c;
}
};
int N, S, T, ecnt;
int head[maxn], cur[maxn], d[maxn];
Edge edge[maxm << 1];
// 节点总个数,节点编号从 0 ~ N - 1
void Init(int n) {
N = n;
ecnt = 0;
memset(head, -1, sizeof(int) * N);
}
void addEdge(int u, int v, int c) {
edge[ecnt] = Edge(v, head[u], 0, c);
head[u] = ecnt++;
edge[ecnt] = Edge(u, head[v], 0, 0);
head[v] = ecnt++;
}
void add_s_t() {
// 将源点和汇点连入图中
}
bool bfs() {
queue<int> que;
for(int i = 0; i < N; ++i) {
d[i] = 0;
}
que.push(S);
d[S] = 1;
while(!que.empty()) {
int tmp = que.front();
que.pop();
for(int id = head[tmp]; id != -1; id = edge[id].Next) {
if(d[edge[id].to] == 0 && edge[id].cap > edge[id].flow) {
d[edge[id].to] = d[tmp] + 1;
que.push(edge[id].to);
}
}
}
return d[T] != 0;
}
int dfs(int x, int res) {
if(x == T || res == 0) {
return res;
}
int ret = 0, tmp;
for(int &id = cur[x]; id != -1; id = edge[id].Next) {
int pos = edge[id].to;
if(d[pos] == d[x] + 1 && edge[id].cap > edge[id].flow) {
tmp = dfs(pos, min(res, edge[id].cap - edge[id].flow));
if(tmp > 0) {
ret += tmp;
res -= tmp;
edge[id].flow += tmp;
edge[id ^ 1].flow -= tmp;
if(res == 0) {
break;
}
}
}
}
return ret;
}
// 调用 Dinic 前需构好图
// 直接调用函数返回 maxflow
int Dinic(int s, int t) {
int flow = 0;
S = s;
T = t;
add_s_t();
while(bfs()) {
for(int i = 0; i < N; ++i) {
cur[i] = head[i];
}
flow += dfs(S, INF);
}
return flow;
}
const int maxn = 1000 + 100;
const int maxm = 20000 + 100;
const int INF = 0x3f3f3f3f;
// 时间复杂度为流量 * 边数 * 常数(2)
// 求最大费用只需要将费用取相反数
// maxm 为需要加边的数量,即调用 addEdge 的总次数
// 使用前需调用 Init 初始化
struct Edge {
int to, Next, cap, flow, cost;
Edge() {}
Edge(int t, int n, int ca, int f, int co) {
to = t;
Next = n;
cap = ca;
flow = f;
cost = co;
}
};
int N;
int head[maxn], ecnt;
int pre[maxn], dis[maxn];
bool inque[maxn];
Edge edge[maxm << 1];
// 节点总个数,节点编号从 0 ~ N - 1
void Init(int n) {
N = n;
ecnt = 0;
memset(head, -1, sizeof(int) * N);
}
void addEdge(int u, int v, int cap, int cost) {
edge[ecnt] = Edge(v, head[u], cap, 0, cost);
head[u] = ecnt++;
edge[ecnt] = Edge(u, head[v], 0, 0, -cost);
head[v] = ecnt++;
}
void add_s_t() {
// 将源点和汇点连入图中
}
bool spfa(int s, int t) {
queue<int> que;
for(int i = 0; i < N; i++) {
dis[i] = INF;
inque[i] = false;
pre[i] = -1;
}
dis[s] = 0;
inque[s] = true;
que.push(s);
while(!que.empty()) {
int u = que.front();
que.pop();
inque[u] = false;
for(int i = head[u]; i != -1; i = edge[i].Next) {
int v = edge[i].to;
if(edge[i].cap > edge[i].flow && dis[v] > dis[u] + edge[i].cost) {
dis[v] = dis[u] + edge[i].cost;
pre[v] = i;
if(!inque[v]) {
inque[v] = true;
que.push(v);
}
}
}
}
return pre[t] != -1;
}
// 调用 minCostMaxflow 前需构好图
// 返回的是 maxflow,cost存的是最小费用
int minCostMaxflow(int s, int t, int &cost) {
int flow = 0;
cost = 0;
add_s_t();
while(spfa(s,t)) {
int Min = INF;
for(int i = pre[t]; i != -1; i = pre[edge[i ^ 1].to]) {
if(Min > edge[i].cap - edge[i].flow) {
Min = edge[i].cap - edge[i].flow;
}
}
for(int i = pre[t]; i != -1; i = pre[edge[i^1].to]) {
edge[i].flow += Min;
edge[i ^ 1].flow -= Min;
cost += edge[i].cost * Min;
}
flow += Min;
}
return flow;
}