版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39972971/article/details/83241357
【比赛链接】
【题解链接】
**【A】**Elevator or Stairs?
【思路要点】
- 按照题意计算两种方式的用时,取较优的方案采纳。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e5 + 5; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int main() { int x, y, z, t1, t2, t3; read(x), read(y), read(z), read(t1), read(t2), read(t3); int s = abs(x - y) * t1; int e = abs(x - z) * t2 + abs(x - y) * t2 + t3 * 3; if (e <= s) printf("YES\n"); else printf("NO\n"); return 0; }
**【B】**Appending Mex
【思路要点】
- 显然在操作合法的情况下,序列中的数形成的集合是一个仅包含最小的 个自然数的集合,因此,可能加入的数应当是 到 中的一个整数,按照此判断标准检验操作序列的合法性即可。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e5 + 5; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int main() { int n, Max = -1; read(n); for (int i = 1; i <= n; i++) { int x; read(x); if (x > Max + 1) { printf("%d\n", i); return 0; } chkmax(Max, x); } printf("-1\n"); return 0; }
**【C】**Candies Distribution
【思路要点】
- 计算 , 表示大于 的数的个数。
- 因此,实际上所有 的大小关系已经确定了,我们根据 构造出一组符合条件的 ,再检验其是否满足 和 的限制即可。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e5 + 5; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int n, a[MAXN], l[MAXN], r[MAXN]; int rnk[MAXN], pos[MAXN]; bool cmp(int x, int y) { return l[x] + r[x] < l[y] + r[y]; } int main() { read(n); for (int i = 1; i <= n; i++) read(l[i]); for (int i = 1; i <= n; i++) read(r[i]); for (int i = 1; i <= n; i++) pos[i] = i; sort(pos + 1, pos + n + 1, cmp); for (int i = 1; i <= n; i++) { int now = pos[i], last = pos[i - 1]; if (i == 1 || l[now] + r[now] != l[last] + r[last]) rnk[i] = i; else rnk[i] = rnk[i - 1]; a[now] = n - rnk[i] + 1; } for (int i = 1; i <= n; i++) { int cnt = 0; for (int j = 1; j <= i - 1; j++) if (a[j] > a[i]) cnt++; if (cnt != l[i]) { printf("NO\n"); return 0; } cnt = 0; for (int j = i + 1; j <= n; j++) if (a[j] > a[i]) cnt++; if (cnt != r[i]) { printf("NO\n"); return 0; } } printf("YES\n"); for (int i = 1; i <= n; i++) printf("%d ", a[i]); return 0; }
**【D】**Changing Array
【思路要点】
- 令 为 的前缀异或和,一个区间 的区间异或和即为 ,区间 的区间异或和为 当且仅当 。
- 题目中对一个数 进行取反操作相当于执行 ,这样的操作对 的影响是对 数组的某一段后缀执行 。因此,我们使用该操作可以任意地对 执行 ,最后我们希望最小化 的 对数,那么对于 ,显然将等于 的 和等于 的 的个数修改得尽量平均是最优的。
- 时间复杂度 。
- 由该做法,我们也可以证明下面一个贪心算法的正确性,即:从左向右考虑每一个区间的右端点 ,若执行 会使得 中区间异或和为 的变少,则执行之。
- 两个算法在本质上是一样的,以下代码实现了上述贪心算法。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e5 + 5; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } map <int, int> mp; int n, k, a[MAXN]; int main() { read(n), read(k); for (int i = 1; i <= n; i++) read(a[i]); int goal = (1 << k) - 1; ll ans = n * (n + 1ll) / 2; for (int i = 1; i <= n; i++) { a[i] ^= a[i - 1], mp[a[i - 1]]++; int tmp = min(mp[a[i]], mp[goal ^ a[i]]); ans -= tmp; if (tmp == mp[goal ^ a[i]]) a[i] ^= goal; } writeln(ans); return 0; }
**【E】**Chips Puzzle
【思路要点】
- 题目给定了 次操作,不妨考虑用 次操作将起始状态化为一个对于所有起始状态统一的中间状态,再用 次操作由中间状态得到目标状态。
- 我们选取将所有白子移动至左上角,所有黑子移动至右下角的状态为中间状态。
- 将起始状态化为中间状态的方式:将棋盘边缘上的所有棋子花费 步移动至右上角或左下角,再将这两个角落中的棋子花费 步归类至左上角或右下角。现在,棋盘边缘上只有左上角和右下角有棋子,对于中间的每一枚棋子,花费 步将其经棋盘边缘归类至左上角或右下角即可。
- 将中间状态化为目标状态的方式:借助棋盘边缘花费 步完成对棋盘中部的构造,然后借助右上角或左下角花费 步完成对棋盘边缘除了四角处的构造,进而借助右上角或左下角花费 步完成对左上角和右下角的构造,最后花费 步完成对右上角和左下角的构造。
- 时间复杂度 ,使用操作次数不超过 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 305; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int n, m; string a[MAXN][MAXN], b[MAXN][MAXN]; vector <pair <int, int> > from, to; void moveto(int sx, int sy, int tx, int ty) { if (sx == tx && sy == ty) return; if (sx != tx) { from.emplace_back(sx, sy); to.emplace_back(tx, sy); moveto(tx, sy, tx, ty); } else { from.emplace_back(sx, sy); to.emplace_back(sx, ty); moveto(sx, ty, tx, ty); } } bool valid(int x, int y) { if (x == 1 && y == m) return true; if (x == n && y == 1) return true; return false; } void Moveto(int sx, int sy, int tx, int ty) { if (sx == tx && sy == ty) return; if (sx != tx && valid(tx, sy)) { from.emplace_back(sx, sy); to.emplace_back(tx, sy); Moveto(tx, sy, tx, ty); } else if (sy != ty && valid(sx, ty)) { from.emplace_back(sx, sy); to.emplace_back(sx, ty); Moveto(sx, ty, tx, ty); } else moveto(sx, sy, tx, ty); } int main() { read(n), read(m); for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { cin >> a[i][j]; reverse(a[i][j].begin(), a[i][j].end()); } for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { cin >> b[i][j]; reverse(b[i][j].begin(), b[i][j].end()); } for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { if (i == 1 && j == m) continue; if (i == n && j == 1) continue; if (i == 1 || j == m) { a[1][m] += a[i][j]; for (auto x : a[i][j]) moveto(i, j, 1, m); a[i][j].clear(); } else if (i == n || j == 1) { a[n][1] += a[i][j]; for (auto x : a[i][j]) moveto(i, j, n, 1); a[i][j].clear(); } } for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { for (auto x : a[i][j]) if (x == '0') moveto(i, j, 1, 1); else moveto(i, j, n, m); } for (int i = 2; i <= n - 1; i++) for (int j = 2; j <= m - 1; j++) { for (auto x : b[i][j]) if (x == '0') moveto(1, 1, i, j); else moveto(n, m, i, j); b[i][j].clear(); } for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { if (i == 1 && j == 1) continue; if (i == n && j == 1) continue; if (i == 1 && j == m) continue; if (i == n && j == m) continue; for (auto x : b[i][j]) if (x == '0') Moveto(1, 1, i, j); else Moveto(n, m, i, j); b[i][j].clear(); } for (auto x : b[1][1]) if (x == '0') moveto(1, 1, 1, m), moveto(1, m, 1, 1); else moveto(n, m, 1, 1); for (auto x : b[n][m]) if (x == '0') moveto(1, 1, n, m); else moveto(n, m, 1, m), moveto(1, m, n, m); for (auto x : b[1][m]) if (x == '0') moveto(1, 1, 1, m); else moveto(n, m, 1, m); for (auto x : b[n][1]) if (x == '0') moveto(1, 1, n, 1); else moveto(n, m, n, 1); writeln(from.size()); for (unsigned i = 0; i < from.size(); i++) printf("%d %d %d %d\n", from[i].first, from[i].second, to[i].first, to[i].second); return 0; }
**【F】**Electric Scheme
【思路要点】
- 显然的一组合法解是在每一个火花处连接两根不同的电线。
- 我们可以连接相邻的两根在同一直线上的同向电线,每连接一处,就可以使答案变优 ,但同时我们需要保证连接的电线不会相交。可能连接方式总数是 的。
- 若两种连接方式产生相交,我们称这两种方式不兼容。显然同向的连接方式不会不兼容,因此“兼容”关系构成了一张二分图,求解其最大独立集即可。
- 时间复杂度 ,实测跑得很快。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e3 + 5; const int MAXP = 2e3 + 5; const int INF = 1e9; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } struct edge {int dest, flow; unsigned pos; }; vector <edge> a[MAXP]; unsigned curr[MAXP]; int tot, s, t, dist[MAXP]; void addedge(int x, int y, int z) { a[x].push_back((edge) {y, z, a[y].size()}); a[y].push_back((edge) {x, 0, a[x].size() - 1}); } int dinic(int pos, int limit) { if (pos == t) return limit; int used = 0, tmp; for (unsigned &i = curr[pos]; i < a[pos].size(); i++) if (a[pos][i].flow != 0 && dist[pos] + 1 == dist[a[pos][i].dest] && (tmp = dinic(a[pos][i].dest, min(limit - used, a[pos][i].flow)))) { used += tmp; a[pos][i].flow -= tmp; a[a[pos][i].dest][a[pos][i].pos].flow += tmp; if (used == limit) return used; } return used; } bool bfs() { static int q[MAXP]; int l = 0, r = 0; memset(dist, 0, sizeof(dist)); dist[s] = 1, q[0] = s; while (l <= r) { int tmp = q[l]; for (unsigned i = 0; i < a[tmp].size(); i++) if (dist[a[tmp][i].dest] == 0 && a[tmp][i].flow != 0) { q[++r] = a[tmp][i].dest; dist[q[r]] = dist[tmp] + 1; } l++; } return dist[t] != 0; } int n, x[MAXN], y[MAXN], r[MAXN], d[MAXN]; int pd[MAXN], pr[MAXN]; int main() { read(n); for (int i = 1; i <= n; i++) read(x[i]), read(y[i]); s = 0, t = tot = 1; int ans = n * 2; for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { if (x[i] == x[j] && y[j] < y[i] && (d[i] == 0 || y[j] > y[d[i]])) d[i] = j; if (y[i] == y[j] && x[j] > x[i] && (r[i] == 0 || x[j] < x[r[i]])) r[i] = j; } if (d[i]) pd[i] = ++tot, ans--, addedge(s, tot, 1); if (r[i]) pr[i] = ++tot, ans--, addedge(tot, t, 1); } for (int i = 1; i <= n; i++) { if (!d[i]) continue; for (int j = 1; j <= n; j++) { if (!r[j]) continue; int px = x[i], py = y[j]; if (px > x[j] && px < x[r[j]] && py > y[d[i]] && py < y[i]) addedge(pd[i], pr[j], INF); } } while (bfs()) { memset(curr, 0, sizeof(curr)); ans += dinic(s, INF); } cerr << ans << endl; static vector <pair <int, int> > from, to; static bool head[MAXN]; memset(head, true, sizeof(head)); from.clear(), to.clear(); for (int i = 1; i <= n; i++) { if (r[i] && dist[pr[i]] != 0) r[i] = 0; if (r[i]) head[r[i]] = false; } for (int i = 1; i <= n; i++) { if (!head[i]) continue; int pos = i; while (r[pos]) pos = r[pos]; from.emplace_back(x[i], y[i]); to.emplace_back(x[pos], y[pos]); } writeln(from.size()); for (unsigned i = 0; i < from.size(); i++) printf("%d %d %d %d\n", from[i].first, from[i].second, to[i].first, to[i].second); memset(head, true, sizeof(head)); from.clear(), to.clear(); for (int i = 1; i <= n; i++) { if (d[i] && dist[pd[i]] == 0) d[i] = 0; if (d[i]) head[d[i]] = false; } for (int i = 1; i <= n; i++) { if (!head[i]) continue; int pos = i; while (d[pos]) pos = d[pos]; from.emplace_back(x[i], y[i]); to.emplace_back(x[pos], y[pos]); } writeln(from.size()); for (unsigned i = 0; i < from.size(); i++) printf("%d %d %d %d\n", from[i].first, from[i].second, to[i].first, to[i].second); return 0; }
**【G】**New Road Network
【思路要点】
- 令 表示包含节点 的社区集合。
- 若一个社区仅包含 个节点,那么删去它不会产生影响。
- 否则,一个社区将包含至少 个节点,这意味着若树上的一个叶子结点 的父亲为 ,应当有 。并且,我们能够说明,当 ,并且存在可行解,我们必然能够将该解其调整至 为一个叶子结点,且其父亲为 。
- 上述观察给出了一个 的做法。
- 接下来便是本题厉害的地方:若我们构建一张无向图, 与 之间的边边权为 ,上述算法本质上是在寻找这张图的一棵最大生成树,因此,我们求解一棵最大生成树,再检验其是否合法即可。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e3 + 5; const int MAXM = MAXN * MAXN; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int n, m, f[MAXN], ansx[MAXN], ansy[MAXN]; int x[MAXM], y[MAXM], len[MAXM], pos[MAXM]; bitset <MAXN> a[MAXN], b[MAXN], now; vector <int> e[MAXN]; int F(int x) { if (f[x] == x) return x; else return f[x] = F(f[x]); } void work(int pos) { if (now[pos]) now[pos] = 0; else return; for (auto x : e[pos]) work(x); } bool check() { for (int i = 1; i <= n; i++) e[i].clear(); for (int i = 1; i <= n - 1; i++) { e[ansx[i]].push_back(ansy[i]); e[ansy[i]].push_back(ansx[i]); } for (int i = 1; i <= m; i++) { now = a[i]; work(now._Find_first()); if (now.count()) return false; } return true; } int main() { int T; read(T); while (T--) { read(n), read(m); for (int i = 1; i <= n || i <= m; i++) a[i].reset(), b[i].reset(); for (int i = 1; i <= m; i++) { static char s[MAXN]; scanf("\n%s", s + 1); for (int j = 1; j <= n; j++) a[i][j] = b[j][i] = s[j] - '0'; } int tot = 0; for (int i = 1; i <= n; i++) for (int j = i + 1; j <= n; j++) { tot++, pos[tot] = tot; x[tot] = i, y[tot] = j, len[tot] = (b[i] & b[j]).count(); } sort(pos + 1, pos + tot + 1, [&] (int x, int y) {return len[x] > len[y];}); for (int i = 1; i <= n; i++) f[i] = i; int cnt = 0; for (int i = 1; i <= tot; i++) { int tmp = pos[i]; if (F(x[tmp]) != F(y[tmp])) { f[F(x[tmp])] = F(y[tmp]), cnt++; ansx[cnt] = x[tmp]; ansy[cnt] = y[tmp]; } } if (check()) { printf("YES\n"); for (int i = 1; i <= cnt; i++) printf("%d %d\n", ansx[i], ansy[i]); } else printf("NO\n"); } return 0; }
**【H】**Epic Convolution
【思路要点】
- 首先,模数 ,为一个质数, 。
- 令 ,若能求出 ,那么答案即为 。
- 由中国剩余定理,考虑 模 的余数,即考虑 模 分别的余数。
- 由于 均为奇质数,它们分别存在原根。 存在原根 , 存在原根 。因此,我们分别取 和 的离散对数能够将下标上的乘法转化为加法,而模 意义下的乘法相当于 运算。
- 于是,问题转化为了求 。在第一维上 ,第二、第三维上 即可。
- 上述算法还有个小问题:在 或 是 或 的倍数时,不存在符合要求离散对数,但这样的位置至多只有 个,因此在这些位置上暴力即可。
- 时间复杂度 ,其中 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e5 + 5; const int P = 490019; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int power(int x, int y, int p) { if (y == 0) return 1; int tmp = power(x, y / 2, p); if (y % 2 == 0) return 1ll * tmp * tmp % p; else return 1ll * tmp * tmp % p * x % p; } void update(int &x, int y) { x += y; if (x >= P) x -= P; } struct point {long double x, y; }; point operator + (point a, point b) {return (point) {a.x + b.x, a.y + b.y}; } point operator - (point a, point b) {return (point) {a.x - b.x, a.y - b.y}; } point operator * (point a, point b) {return (point) {a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x}; } point operator / (point a, long double x) {return (point) {a.x / x, a.y / x}; } namespace FFT { const int MAXN = 1024; const long double pi = acos(-1); int N, Log, home[MAXN]; void FFTinit() { N = 1024, Log = 10; for (int i = 0; i < N; i++) { int tmp = i, ans = 0; for (int j = 1; j <= Log; j++) { ans <<= 1; ans += tmp & 1; tmp >>= 1; } home[i] = ans; } } void FFT(point *a, int mode) { for (int i = 0; i < N; i++) if (home[i] < i) swap(a[i], a[home[i]]); for (int len = 2; len <= N; len <<= 1) { point delta = (point) {cos(2 * pi / len * mode), sin(2 * pi / len * mode)}; for (int i = 0; i < N; i += len) { point now = (point) {1, 0}; for (int j = i, k = i + len / 2; k < i + len; j++, k++) { point tmp = a[j]; point tnp = a[k] * now; a[j] = tmp + tnp; a[k] = tmp - tnp; now = now * delta; } } } if (mode == -1) { for (int i = 0; i < N; i++) a[i] = a[i] / N; } } } int n, m, v, val[MAXN], vbl[MAXN]; bool flga[MAXN], flgb[MAXN]; int merg[2][1024][1024]; int a[2][1024][1024], b[2][1024][1024]; int c[P]; int p491[1024], p499[1024], log491[1024], log499[1024]; void multiply() { FFT :: FFTinit(); static point res[2][1024][1024], tmp[1024], tnp[1024]; static point ta[2][1024][1024], tb[2][1024][1024]; for (int i = 0; i <= 1; i++) for (int j = 0; j <= 1023; j++) { for (int k = 0; k <= 1023; k++) { tmp[k] = (point) {(double) a[i][j][k], 0}; tnp[k] = (point) {(double) b[i][j][k], 0}; } FFT :: FFT(tmp, 1); FFT :: FFT(tnp, 1); for (int k = 0; k <= 1023; k++) { ta[i][j][k] = tmp[k]; tb[i][j][k] = tnp[k]; } } for (int i = 0; i <= 1; i++) for (int k = 0; k <= 1023; k++) { for (int j = 0; j <= 1023; j++) { tmp[j] = ta[i][j][k]; tnp[j] = tb[i][j][k]; } FFT :: FFT(tmp, 1); FFT :: FFT(tnp, 1); for (int j = 0; j <= 1023; j++) { ta[i][j][k] = tmp[j]; tb[i][j][k] = tnp[j]; } } for (int i = 0; i <= 1023; i++) for (int j = 0; j <= 1023; j++) { ta[0][i][j] = ta[0][i][j] + ta[1][i][j]; tb[0][i][j] = tb[0][i][j] + tb[1][i][j]; res[0][i][j] = ta[0][i][j] * tb[0][i][j]; res[1][i][j] = ta[1][i][j] * tb[1][i][j]; } for (int i = 0; i <= 1; i++) for (int j = 0; j <= 1023; j++) { for (int k = 0; k <= 1023; k++) tmp[k] = res[i][j][k]; FFT :: FFT(tmp, -1); for (int k = 0; k <= 1023; k++) res[i][j][k] = tmp[k]; } for (int i = 0; i <= 1; i++) for (int k = 0; k <= 1023; k++) { for (int j = 0; j <= 1023; j++) tmp[j] = res[i][j][k]; FFT :: FFT(tmp, -1); for (int j = 0; j <= 1023; j++) res[i][j][k] = tmp[j]; } for (int i = 0; i <= 1023; i++) for (int j = 0; j <= 1023; j++) { res[0][i][j] = res[0][i][j] - res[1][i][j]; update(c[merg[0][p491[i]][p499[j]]], (ll) (res[0][i][j].x + 0.5) % P); update(c[merg[1][p491[i]][p499[j]]], (ll) (res[1][i][j].x + 0.5) % P); } } int main() { read(n), read(m), read(v); for (int i = 0, ta = 1, tb = 1; i <= 1023; i++, ta = ta * 2 % 491, tb = tb * 7 % 499) { p491[i] = ta, p499[i] = tb; if (i <= 491 - 2) log491[ta] = i; if (i <= 499 - 2) log499[tb] = i; } for (int i = 0; i <= P - 1; i++) { assert(merg[i % 2][i % 491][i % 499] == 0); merg[i % 2][i % 491][i % 499] = i; } for (int i = 0; i <= n - 1; i++) { read(val[i]); int tmp = 1ll * i * i % (P - 1); if (tmp % 491 && tmp % 499) update(a[tmp & 1][log491[tmp % 491]][log499[tmp % 499]], val[i]); else flga[i] = true; } for (int i = 0; i <= m - 1; i++) { read(vbl[i]); int tmp = 1ll * i * i * i % (P - 1); if (tmp % 491 && tmp % 499) update(b[tmp & 1][log491[tmp % 491]][log499[tmp % 499]], vbl[i]); else flgb[i] = true; } multiply(); for (int i = 0; i <= n - 1; i++) { if (!flga[i]) continue; for (int j = 0; j <= m - 1; j++) { int tmp = 1ll * i * i % (P - 1) * j % (P - 1) * j % (P - 1) * j % (P - 1); update(c[tmp], 1ll * val[i] * vbl[j] % P); } } for (int j = 0; j <= m - 1; j++) { if (!flgb[j]) continue; for (int i = 0; i <= n - 1; i++) { if (flga[i]) continue; int tmp = 1ll * i * i % (P - 1) * j % (P - 1) * j % (P - 1) * j % (P - 1); update(c[tmp], 1ll * val[i] * vbl[j] % P); } } int ans = 0; for (int i = 0; i <= P - 1; i++) update(ans, 1ll * c[i] * power(v, i, P) % P); writeln(ans); return 0; }