ll bsgs(ll b, int n) { map<ll, int> mp; n %= P; ll t = sqrt(P) + 1; for (int i = 0; i < t; i++) mp[n * qpow(b, i) % P] = i; b = qpow(b, t); if (!b) return !n ? 1 : -1; for (int i = 1; i <= t; i++) { ll x = qpow(b, i); int j = mp.count(x) ? mp[x] : -1; if (~j && i * t - j >= 0) return i * t - j; } return-1; }
由于异或操作的良好性质,我们先钦定一条从 1 到 n 的路径,然后考虑往路径上面加环,显然我们从路径上某个点,走到环上的某个点,绕完整个环,再沿着同样的路走回来,只有那个环上的路径异或和会被计入答案,任意一条可能的路径都可以被表示为这样的形式。然后用线性基处理所有环异或起来的最大值即可。正确性可能不显然,感性理解一下。
lll Pollard_Rho(lll n) { if (Miller_Rabin(n)) return-1; if (n == 4) return2; uniform_int_distribution<uint64_t> d(1, n - 1); while (true) { lll c = d(eng); auto f = [c, n](lll y) { return (y * y % n + c) % n; }; lll x = f(0), y = f(f(0)); while (x != y) { lll z = gcd(abs(x - y), n); if (z > 1) return z; x = f(x), y = f(f(y)); } } }
为了表示方便,令 ci=axi
由于一些取模的神秘原因(我不会,但不这么做不对),对于要处理的 bi 和 ci ,先消去共同的 M 的质因数。
和上一题一样,不过这次由于物品在格边上,故不用拆点,不过注意到一个物品只能被取一次,可以这样操作:一条边从 u 到 v (有方向限制),物品价值 w,先建一条 (1,w) ,再建一条边 (∞,0) ,可以保证只取一次。然后从 s 向所有的起点连边 (x,0) ,其中 x 为起点可以让多少个机器人出发,终点同理。
vector<pair<int, int>> edges_new[MAXN]; voidexport_paths(int lim)// lim 为机器人走到终点的个数,即最大流 { // 先建成一个新图,方便处理 for (int i = 1; i <= n * m * 2; i++) { if (i % 2 == 1) continue; for (int j = head[i]; j; j = edges[j].nxt) { int v = edges[j].v; if (edges[j ^ 1].flw && v != i - 1 && v != s && v != t) { edges_new[i == s ? s : i / 2].push_back({v == t ? t : (v + 1) / 2, edges[j ^ 1].flw}); } } } for (int i = 1; i <= lim; i++) { int u = 1; while (u != n * m) { for (auto& p : edges_new[u]) { // p.second 为这条边最多经过机器人次数 if (p.second) { if (p.first == u + 1) { printf("%d %d\n", i, 1); } else { assert(p.first == u + m); printf("%d %d\n", i, 0); } p.second--; // 改边权 u = p.first; break; } } } } }
structgraph_t { structedge_t { // 链式前向星 int v, nxt, flw; // 实现上为了方便,直接修改流量 } edges[MAXM]; int head[MAXN], e_cnt = 1; // u^1 得到 u 的反向边
void _add(int u, int v, int f) { edges[++e_cnt] = {v, head[u], f}, head[u] = e_cnt; } voidadd(int u, int v, int f){ _add(u, v, f), _add(v, u, 0); }
int dis[MAXN], now[MAXN]; // 当前弧优化,从未流尽的边开始考虑 boolbfs() { memset(dis, 0x3f, sizeof(dis)); memcpy(now, head, sizeof(now)); queue<int> q; q.push(s), dis[s] = 0; while (q.size()) { int u = q.front(); q.pop(); for (int i = head[u]; i; i = edges[i].nxt) { if (edges[i].flw && dis[edges[i].v] > dis[u] + 1) { // 分层 dis[edges[i].v] = dis[u] + 1; q.push(edges[i].v); } } } return dis[t] < INF; }
intdfs(int u, int flow) { if (u == t || !flow) return flow; int maxflow = 0; for (int i = now[u]; i && flow; now[u] = i = edges[i].nxt) { int v = edges[i].v; if (edges[i].flw && dis[v] == dis[u] + 1) { // 一层一层来 int k = dfs(v, min(flow, edges[i].flw)); if (!k) dis[v] = INF; // 删去流尽的边 maxflow += k, flow -= k, edges[i].flw -= k, edges[i ^ 1].flw += k; // 应用修改 } } return maxflow; }
intdinic() { int maxflow = 0, x = 0; while (bfs()) { while ((x = dfs(s, INF))) maxflow += x; } return maxflow; } } G;
时间复杂度证明
最坏时间复杂度 O(n2m) ,大多数情况远远跑不满 。
考虑每次增广,在使用当前弧优化的情况下,对于每个点,显然当前弧 now 至多变化 m 次,共 n 个点,故单次时间复杂度 O(nm) 。
structgraph_t { structedge_t { int v, nxt, flw; } edges[MAXM]; int head[MAXN], e_cnt = 1;
void _add_edge(int u, int v, int f) { edges[++e_cnt] = {v, head[u], f}, head[u] = e_cnt; } voidadd_edge(int u, int v, int f){ _add_edge(u, v, f), _add_edge(v, u, 0); }
// 从汇点开始标记高度 boolbfs() { memset(h, 0, sizeof(h)); queue<int> q; q.push(t); h[t] = 0; while (q.size()) { int u = q.front(); q.pop(); for (int i = head[u]; i; i = edges[i].nxt) { int v = edges[i].v; if (edges[i ^ 1].flw && h[v] > h[u] + 1) { h[v] = h[u] + 1; q.push(v); } } } if (h[s] < 1e9) { for (int i = 1; i <= n; i++) { if (h[i] >= 1e9) h[i] = n + 100; } return h[s] = n, true; } elsereturnfalse; }
// 向高度正好小 1 的点流超额流 voidpush(int u) { for (int i = head[u]; i && val[u]; i = edges[i].nxt) { int v = edges[i].v; if (edges[i].flw && h[v] == h[u] - 1) { int k = min(edges[i].flw, val[u]); edges[i].flw -= k, edges[i ^ 1].flw += k; val[u] -= k, val[v] += k; if (v != s && v != t && !in_q[v]) q.push({h[v], v}), in_q[v] = true; } } }
// 重贴标签,让 h 刚好比相连的 h 最小的大 1 voidrelabel(int u) { h[u] = 1e9; for (int i = head[u]; i; i = edges[i].nxt) { int v = edges[i].v; if (edges[i].flw && h[u] > h[v] + 1) h[u] = h[v] + 1; } if (h[u] >= 1e9) h[u] = n + 100; }
inthlpp() { if (!bfs()) return0; // 高度为 h 的有多少个 for (int i = 1; i <= n; i++) gap[h[i]]++; // 从源点推出,忽略高度 for (int i = head[s]; i; i = edges[i].nxt) { int v = edges[i].v; if (edges[i].flw) { int k = edges[i].flw; edges[i].flw -= k, edges[i ^ 1].flw += k; val[v] += k; if (v != s && v != t && !in_q[v]) q.push({h[v], v}), in_q[v] = true; } }
while (q.size()) { int u = q.top().second; // 取出高度最高的点 q.pop(); in_q[u] = false; push(u); if (val[u]) { // 没流完 gap[h[u]]--; if (!gap[h[u]]) { // gap 优化 // 比它高的点不能把流量推送到 t 了 // 高度设为 n + 1 尽快推送回 s for (int i = 1; i <= n; i++) { if (i != s && i != t && h[i] > h[u] && h[i] < n + 1) h[i] = n + 1; } } relabel(u); // 重贴标签 gap[h[u]]++; q.push({h[u], u}); in_q[u] = true; } } return val[t]; } } G;
最小割
一个有向图,有边权,它的最小割就是切断一些边,把这个图切成两半,让源点 s 和汇点 t 不存在一条路径,同时割断的边边权和最小。
structgraph_t { structedge_t { int v, nxt, flw, cst; } edges[MAXM]; int head[MAXN], e_cnt = 1; void _add_edge(int u, int v, int f, int c) { edges[++e_cnt] = {v, head[u], f, c}, head[u] = e_cnt; } voidadd_edge(int u, int v, int f, int c){ _add_edge(u, v, f, c), _add_edge(v, u, 0, -c); }
int dis[MAXN], now[MAXN]; bool vis[MAXN]; boolspfa()// 最短路 { memset(dis, 0x3f, sizeof(dis)), memcpy(now, head, sizeof(now)); queue<int> q; q.push(s), dis[s] = 0; while (q.size()) { int u = q.front(); q.pop(); vis[u] = false; for (int i = head[u]; i; i = edges[i].nxt) { int v = edges[i].v; if (edges[i].flw && dis[v] > dis[u] + edges[i].cst) { dis[v] = dis[u] + edges[i].cst; if (!vis[v]) vis[v] = true, q.push(v); } } } return dis[t] < INF; }
pair<int, int> dfs(int u, int flow)// 和最大流差不多 { if (u == t || !flow) return {flow, 0}; int maxflow = 0, mincost = 0; vis[u] = true; for (int i = now[u]; i && flow; now[u] = i = edges[i].nxt) { int v = edges[i].v; if (edges[i].flw && dis[v] == dis[u] + edges[i].cst && !vis[v]) { auto p = dfs(v, min(flow, edges[i].flw)); int x = p.first, y = p.second; if (!x) dis[v] = INF; flow -= x, maxflow += x, edges[i].flw -= x, edges[i ^ 1].flw += x; mincost += y + x * edges[i].cst; // 更新贡献 } } vis[u] = false; return {maxflow, mincost}; }
pair<int, int> dinic() { int maxflow = 0, mincost = 0; while (spfa()) { while (true) { auto p = dfs(s, INF); maxflow += p.first, mincost += p.second; if (!p.first) break; } } return {maxflow, mincost}; } } G;
for (int i = 1; i <= n; i++) { for (int j = i + 1; j <= n; j++) { auto d = mat[j][i] / mat[i][i]; for (int k = i; k <= n; k++) { mat[j][k] = (mat[j][k] - d * mat[i][k] % mod + mod) % mod; } } }
constint MAXN = 4e6 + 10; constdouble PI = acos(-1);
int k, l, n, m; int rev[MAXN]; comp a[MAXN], b[MAXN];
voidsetup() { cin >> k >> l; for (int i = 0; i <= k; i++) cin >> a[i]; for (int i = 0; i <= l; i++) cin >> b[i]; for (n = 1; n <= k + l; n <<= 1, m++) ; }
voidfft(comp* a, int op) { for (int i = 0; i < n; i++) { rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (m - 1)); if (rev[i] > i) swap(a[rev[i]], a[i]); }
for (int p = 1; p < n; p <<= 1) { int len = p * 2; auto wn = comp(cos(2.0 * PI / len), op * sin(2.0 * PI / len)); for (int i = 0; i < n; i += len) { auto w = comp(1, 0); for (int j = 0; j < p; j++, w *= wn) { auto x = a[i + j], y = w * a[i + j + p]; a[i + j] = x + y, a[i + j + p] = x - y; } } } }