版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39972971/article/details/82852167
【比赛链接】
【题解链接】
**【A】**Vasya and Triangle
【思路要点】
- 任何三格点角形的面积均是 的整数倍,因此当 不是 的倍数,问题无解。
- 否则,令 。
- 选取 和 中合法的一个作为答案。
- 时间复杂度 。
【代码】
#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(""); } ll n, m, k; ll gcd(ll a, ll b) { if (b == 0) return a; else return gcd(b, a % b); } int main() { read(n), read(m), read(k); ll g = gcd(n, k); k = k / g; if (m * 2 % k != 0) printf("NO\n"); else { ll x = n / g, y = m * 2 / k; if (y > m) x *= 2, y /= 2; printf("YES\n"); printf("0 0\n"); cout << x << ' '<< 0 << endl; cout << 0 << ' ' << y << endl; } }
**【B】**Vasya and Good Sequences
【思路要点】
- 令 表示 二进制表示中 的个数。
- 一个序列 合法当且仅当 且 是 的倍数。
- 注意到 ,枚举左端点 ,枚举满足 的右端点,用后缀和处理剩余的 。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 3e5 + 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 a[MAXN], s[MAXN][2], sum[MAXN], nxt[MAXN]; int main() { int n; read(n); for (int i = 1; i <= n; i++) { ll tmp; read(tmp); while (tmp) { a[i] += tmp & 1; tmp >>= 1; } } for (int i = 1; i <= n; i++){ sum[i] = sum[i - 1] + a[i]; s[i][1] = s[i - 1][1]; s[i][0] = s[i - 1][0]; if (sum[i] % 2 == 1) s[i][1]++; else s[i][0]++; } int las = n + 1; for (int i = n; i >= 0; i--){ nxt[i] = las; if (a[i] != 0) las = i; } ll ans = 0; for (int i = 1; i <= n; i++){ int tmp = sum[i - 1] % 2; int las = i, Max = a[i], sum = a[i]; for (int j = 1; j <= 128; j++){ int id = nxt[las]; if (sum / 2 >= Max){ ans = ans + s[id - 1][tmp] - s[las - 1][tmp]; } Max = max(a[id], Max); sum = sum + a[id]; las = id; if (las > n) break; } ans = ans + s[n][tmp] - s[las - 1][tmp]; } writeln(ans); return 0; }
**【C】**Putting Boxes Together
【思路要点】
- 每次移动必然会有一个物品不动。
- 这个物品一定是处在权值 处的一个物品(或两个物品中的任意一个),通过线段树上二分计算得到该点。
- 用线段树维护区间权值和、权值乘下标和、权值乘位置和,计算询问的答案即可。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e5 + 5; const int P = 1e9 + 7; 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 SegmentTree { struct Node { int lc, rc; long long sum, prd, num; } a[MAXN * 2]; int n, size, root; int pos[MAXN], val[MAXN]; void update(int root) { a[root].sum = a[a[root].lc].sum + a[a[root].rc].sum; a[root].prd = (a[a[root].lc].prd + a[a[root].rc].prd) % P; a[root].num = (a[a[root].lc].num + a[a[root].rc].num) % P; } void build(int &root, int l, int r) { root = ++size; if (l == r) { a[root].sum = val[l]; a[root].num = 1ll * val[l] * l % P; a[root].prd = 1ll * val[l] * pos[l] % P; return; } int mid = (l + r) / 2; build(a[root].lc, l, mid); build(a[root].rc, mid + 1, r); update(root); } void init(int x) { n = x; root = size = 0; for (int i = 1; i <= n; i++) read(pos[i]); for (int i = 1; i <= n; i++) read(val[i]); build(root, 1, n); } void modify(int root, int l, int r, int p) { if (l == r) { a[root].sum = val[l]; a[root].num = 1ll * val[l] * l % P; a[root].prd = 1ll * val[l] * pos[l] % P; return; } int mid = (l + r) / 2; if (mid >= p) modify(a[root].lc, l, mid, p); else modify(a[root].rc, mid + 1, r, p); update(root); } void modify(int pos, int d) { val[pos] = d; modify(root, 1, n, pos); } ll querys(int root, int l, int r, int ql, int qr) { if (l == ql && r == qr) return a[root].sum; long long ans = 0; int mid = (l + r) / 2; if (mid >= ql) ans += querys(a[root].lc, l, mid, ql, min(mid, qr)); if (mid + 1 <= qr) ans += querys(a[root].rc, mid + 1, r, max(mid + 1, ql), qr); return ans; } ll querys(int l, int r) { if (l > r) return 0; else return querys(root, 1, n, l, r); } ll queryp(int root, int l, int r, int ql, int qr) { if (l == ql && r == qr) return a[root].prd; long long ans = 0; int mid = (l + r) / 2; if (mid >= ql) ans += queryp(a[root].lc, l, mid, ql, min(mid, qr)); if (mid + 1 <= qr) ans += queryp(a[root].rc, mid + 1, r, max(mid + 1, ql), qr); return ans % P; } ll queryp(int l, int r) { if (l > r) return 0; else return queryp(root, 1, n, l, r); } ll queryn(int root, int l, int r, int ql, int qr) { if (l == ql && r == qr) return a[root].num; long long ans = 0; int mid = (l + r) / 2; if (mid >= ql) ans += queryn(a[root].lc, l, mid, ql, min(mid, qr)); if (mid + 1 <= qr) ans += queryn(a[root].rc, mid + 1, r, max(mid + 1, ql), qr); return ans % P; } ll queryn(int l, int r) { if (l > r) return 0; else return queryn(root, 1, n, l, r); } int findmid(int root, int l, int r, ll pos) { if (l == r) return l; int mid = (l + r) / 2; if (pos <= a[a[root].lc].sum) return findmid(a[root].lc, l, mid, pos); else return findmid(a[root].rc, mid + 1, r, pos - a[a[root].lc].sum); } ll mquery(int l, int r) { ll tmp = querys(l, r); ll tnp = querys(1, l - 1) + (tmp + 1) / 2; int mid = findmid(root, 1, n, tnp); ll ls = querys(l, mid) % P, rs = querys(mid, r) % P; ll ans = ls * pos[mid] % P - queryp(l, mid); ans += queryp(mid, r) - rs * pos[mid] % P; ans -= 1ll * mid * ls % P - queryn(l, mid); ans -= queryn(mid, r) - 1ll * mid * rs % P; ans = (ans % P + P) % P; return ans; } } ST; int main() { int n, m; read(n), read(m); ST.init(n); for (int i = 1; i <= m; i++) { int x, y; read(x), read(y); if (x <= 0) ST.modify(-x, y); else writeln(ST.mquery(x, y)); } return 0; }
**【D】**Linear Congruential Generator
【思路要点】
- 质数 一定存在原根。
- 线性数列 存在通项 ,因此,当 为 的原根时,数列的周期为 ,否则,数列的周期为 某个因数。
- 当 , ,数列的周期为 。
- 当 , ,数列的周期为 。
- 当 , ,数列的周期为 。
- 当 , ,数列的周期为 ,且进入周期之前有 个不在周期内的元素。
- 能够被生成的多元组的个数应当为各个数列中:进入周期之前不在周期内的元素个数的最大值 周期长度的最小公倍数。
- 若不考虑 , 的数列,我们应当只会选择周期为 或 的数列,显然有一种从大到小贪心的做法,即优先选取 ,若当前 中以及有 因子,则选取 。
- 考虑 , 的数列,在上述贪心中,若存在一个数列,去掉它, 保持不变,则答案应当 。可以证明,上述贪心中被当做周期为 的数列依然只需要被当做周期为 的数列考虑,周期为 的数列依然只需要被当做周期为 的数列考虑。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e5 + 5; const int MAXV = 2e6 + 5; const int P = 1e9 + 7; 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, tot, prime[MAXV], f[MAXV]; int a[MAXN], Max[MAXV], cnt[MAXV]; bool type[MAXN]; void init(int n) { for (int i = 2; i <= n; i++) { if (f[i] == 0) prime[++tot] = f[i] = i; for (int j = 1; j <= tot && prime[j] <= f[i]; j++) { int tmp = prime[j] * i; if (tmp > n) break; f[tmp] = prime[j]; } } } int main() { read(n); for (int i = 1; i <= n; i++) read(a[i]); init(2e6); sort(a + 1, a + n + 1); reverse(a + 1, a + n + 1); for (int i = 1; i <= n; i++) { if (Max[a[i]]) { type[i] = true; int tmp = a[i] - 1; while (tmp != 1) { int fac = f[tmp], now = 0; while (tmp % fac == 0) { tmp /= fac; now++; } if (now > Max[fac]) { Max[fac] = now; cnt[fac] = 1; } else cnt[fac] += Max[fac] == now; } } else Max[a[i]] = cnt[a[i]] = 1; } int ans = 1; for (int i = 1; i <= 2e6; i++) for (int j = 1; j <= Max[i]; j++) ans = 1ll * ans * i % P; for (int i = 1; i <= n; i++) { bool found = false; if (type[i]) { found = true; int tmp = a[i] - 1; while (tmp != 1) { int fac = f[tmp], now = 0; while (tmp % fac == 0) { tmp /= fac; now++; } found &= Max[fac] > now || cnt[fac] >= 2; } } else found = Max[a[i]] >= 2 || cnt[a[i]] >= 2; if (found) { writeln((ans + 1) % P); return 0; } } writeln(ans); return 0; }
**【E】**Euler tour
【思路要点】
- 一个合法的欧拉序应当满足 ,不存在 使得 ,并且若 ,那么 是偶数。
- 对于满足上述几点的输入,我们可以用下面的算法构造出一组解。
- 、若 ,递归处理区间 ,然后将区间 缩成一个点。
- 、若不出现重复数的区间 元素个数为 ,其中非零元素个数应当不超过 ,否则问题无解。将非零元素个数补足 个,则有以下两种情况:
、存在三个连续的位置 ,将 赋为 ,然后将这三个数看做一个 。
、存在三个连续的位置 ,将 赋为 ,然后将这三个数看做一个 。
、不存在上述连续位置,则序列形如 ,在所有序列中的 处填入该子树的根部即可。- 时间复杂度 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 1e6 + 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 a[MAXN], nxtoccur[MAXN]; int n, pre[MAXN], suf[MAXN]; int now, vis[MAXN]; void error() { printf("no\n"); exit(0); } int getnum() { while (vis[now]) now++; if (now > n) error(); vis[now] = 1; return now; } void solve(int l, int r) { if ((r - l) % 2 != 0) error(); int start = pre[l], endpos = suf[r]; for (int i = suf[start]; i <= r; i = nxtoccur[i] ? nxtoccur[i] : suf[i]) { if (nxtoccur[i]) { int tmp = nxtoccur[i]; if (tmp > r) error(); solve(suf[i], pre[tmp]); int tnp = pre[i]; suf[tnp] = tmp, pre[tmp] = tnp; } } int cnt = 0, all = 0; for (int i = suf[start]; i <= r; i = suf[i]) all++, cnt += a[i] != 0; int goal = (all + 1) / 2; if (cnt > goal) error(); for (int i = suf[suf[start]]; i <= r && cnt < goal; i = suf[i]) { if (a[i] == 0) { cnt++; a[i] = getnum(); } } if (all != 1) { for (int i = suf[start], j = suf[suf[start]]; j <= r; i = suf[i], j = suf[j]) { while (i > suf[start] && a[i] && a[j]) { int tmp = pre[i]; if (a[tmp] == 0) { a[tmp] = a[j]; int tnp = pre[tmp]; suf[tnp] = j, pre[j] = tnp; i = tnp; } else break; } while (j < r && a[i] && a[j]) { int tmp = suf[j]; if (a[tmp] == 0) { a[tmp] = a[i]; int tnp = suf[tmp]; suf[i] = tnp, pre[tnp] = i; if (pre[i] >= l) j = i, i = pre[i]; else j = tnp; } else break; } } for (int i = suf[start]; i <= r; i = suf[i]) if (a[i] == 0) a[i] = a[start]; } else if (cnt == 0) a[suf[start]] = getnum(); endpos = pre[endpos]; suf[start] = endpos; pre[endpos] = start; } int main() { read(n); for (int i = 1; i <= 2 * n - 1; i++) { read(a[i]); suf[i] = i + 1; pre[i] = i - 1; } if (a[1] && a[2 * n - 1] && a[1] != a[2 * n - 1]) error(); if (a[1] == 0 || a[2 * n - 1] == 0) a[1] = a[2 * n - 1] = a[1] + a[2 * n - 1]; vis[0] = 1; for (int i = 2 * n - 1; i >= 1; i--) { if (a[i]) { if (vis[a[i]]) nxtoccur[i] = vis[a[i]]; vis[a[i]] = i; } } suf[0] = 1, pre[2 * n] = 2 * n - 1; solve(1, 2 * n - 1); printf("yes\n"); for (int i = 1; i <= 2 * n - 1; i++) printf("%d ", a[i]); return 0; } ```**【比赛链接】**
【题解链接】
**【A】**Vasya and Triangle
【思路要点】
- 任何三格点角形的面积均是 的整数倍,因此当 不是 的倍数,问题无解。
- 否则,令 。
- 选取 和 中合法的一个作为答案。
- 时间复杂度 。
【代码】
#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(""); } ll n, m, k; ll gcd(ll a, ll b) { if (b == 0) return a; else return gcd(b, a % b); } int main() { read(n), read(m), read(k); ll g = gcd(n, k); k = k / g; if (m * 2 % k != 0) printf("NO\n"); else { ll x = n / g, y = m * 2 / k; if (y > m) x *= 2, y /= 2; printf("YES\n"); printf("0 0\n"); cout << x << ' '<< 0 << endl; cout << 0 << ' ' << y << endl; } }
**【B】**Vasya and Good Sequences
【思路要点】
- 令 表示 二进制表示中 的个数。
- 一个序列 合法当且仅当 且 是 的倍数。
- 注意到 ,枚举左端点 ,枚举满足 的右端点,用后缀和处理剩余的 。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 3e5 + 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 a[MAXN], s[MAXN][2], sum[MAXN], nxt[MAXN]; int main() { int n; read(n); for (int i = 1; i <= n; i++) { ll tmp; read(tmp); while (tmp) { a[i] += tmp & 1; tmp >>= 1; } } for (int i = 1; i <= n; i++){ sum[i] = sum[i - 1] + a[i]; s[i][1] = s[i - 1][1]; s[i][0] = s[i - 1][0]; if (sum[i] % 2 == 1) s[i][1]++; else s[i][0]++; } int las = n + 1; for (int i = n; i >= 0; i--){ nxt[i] = las; if (a[i] != 0) las = i; } ll ans = 0; for (int i = 1; i <= n; i++){ int tmp = sum[i - 1] % 2; int las = i, Max = a[i], sum = a[i]; for (int j = 1; j <= 128; j++){ int id = nxt[las]; if (sum / 2 >= Max){ ans = ans + s[id - 1][tmp] - s[las - 1][tmp]; } Max = max(a[id], Max); sum = sum + a[id]; las = id; if (las > n) break; } ans = ans + s[n][tmp] - s[las - 1][tmp]; } writeln(ans); return 0; }
**【C】**Putting Boxes Together
【思路要点】
- 每次移动必然会有一个物品不动。
- 这个物品一定是处在权值 处的一个物品(或两个物品中的任意一个),通过线段树上二分计算得到该点。
- 用线段树维护区间权值和、权值乘下标和、权值乘位置和,计算询问的答案即可。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e5 + 5; const int P = 1e9 + 7; 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 SegmentTree { struct Node { int lc, rc; long long sum, prd, num; } a[MAXN * 2]; int n, size, root; int pos[MAXN], val[MAXN]; void update(int root) { a[root].sum = a[a[root].lc].sum + a[a[root].rc].sum; a[root].prd = (a[a[root].lc].prd + a[a[root].rc].prd) % P; a[root].num = (a[a[root].lc].num + a[a[root].rc].num) % P; } void build(int &root, int l, int r) { root = ++size; if (l == r) { a[root].sum = val[l]; a[root].num = 1ll * val[l] * l % P; a[root].prd = 1ll * val[l] * pos[l] % P; return; } int mid = (l + r) / 2; build(a[root].lc, l, mid); build(a[root].rc, mid + 1, r); update(root); } void init(int x) { n = x; root = size = 0; for (int i = 1; i <= n; i++) read(pos[i]); for (int i = 1; i <= n; i++) read(val[i]); build(root, 1, n); } void modify(int root, int l, int r, int p) { if (l == r) { a[root].sum = val[l]; a[root].num = 1ll * val[l] * l % P; a[root].prd = 1ll * val[l] * pos[l] % P; return; } int mid = (l + r) / 2; if (mid >= p) modify(a[root].lc, l, mid, p); else modify(a[root].rc, mid + 1, r, p); update(root); } void modify(int pos, int d) { val[pos] = d; modify(root, 1, n, pos); } ll querys(int root, int l, int r, int ql, int qr) { if (l == ql && r == qr) return a[root].sum; long long ans = 0; int mid = (l + r) / 2; if (mid >= ql) ans += querys(a[root].lc, l, mid, ql, min(mid, qr)); if (mid + 1 <= qr) ans += querys(a[root].rc, mid + 1, r, max(mid + 1, ql), qr); return ans; } ll querys(int l, int r) { if (l > r) return 0; else return querys(root, 1, n, l, r); } ll queryp(int root, int l, int r, int ql, int qr) { if (l == ql && r == qr) return a[root].prd; long long ans = 0; int mid = (l + r) / 2; if (mid >= ql) ans += queryp(a[root].lc, l, mid, ql, min(mid, qr)); if (mid + 1 <= qr) ans += queryp(a[root].rc, mid + 1, r, max(mid + 1, ql), qr); return ans % P; } ll queryp(int l, int r) { if (l > r) return 0; else return queryp(root, 1, n, l, r); } ll queryn(int root, int l, int r, int ql, int qr) { if (l == ql && r == qr) return a[root].num; long long ans = 0; int mid = (l + r) / 2; if (mid >= ql) ans += queryn(a[root].lc, l, mid, ql, min(mid, qr)); if (mid + 1 <= qr) ans += queryn(a[root].rc, mid + 1, r, max(mid + 1, ql), qr); return ans % P; } ll queryn(int l, int r) { if (l > r) return 0; else return queryn(root, 1, n, l, r); } int findmid(int root, int l, int r, ll pos) { if (l == r) return l; int mid = (l + r) / 2; if (pos <= a[a[root].lc].sum) return findmid(a[root].lc, l, mid, pos); else return findmid(a[root].rc, mid + 1, r, pos - a[a[root].lc].sum); } ll mquery(int l, int r) { ll tmp = querys(l, r); ll tnp = querys(1, l - 1) + (tmp + 1) / 2; int mid = findmid(root, 1, n, tnp); ll ls = querys(l, mid) % P, rs = querys(mid, r) % P; ll ans = ls * pos[mid] % P - queryp(l, mid); ans += queryp(mid, r) - rs * pos[mid] % P; ans -= 1ll * mid * ls % P - queryn(l, mid); ans -= queryn(mid, r) - 1ll * mid * rs % P; ans = (ans % P + P) % P; return ans; } } ST; int main() { int n, m; read(n), read(m); ST.init(n); for (int i = 1; i <= m; i++) { int x, y; read(x), read(y); if (x <= 0) ST.modify(-x, y); else writeln(ST.mquery(x, y)); } return 0; }
**【D】**Linear Congruential Generator
【思路要点】
- 质数 一定存在原根。
- 线性数列 存在通项 ,因此,当 为 的原根时,数列的周期为 ,否则,数列的周期为 某个因数。
- 当 , ,数列的周期为 。
- 当 , ,数列的周期为 。
- 当 , ,数列的周期为 。
- 当 , ,数列的周期为 ,且进入周期之前有 个不在周期内的元素。
- 能够被生成的多元组的个数应当为各个数列中:进入周期之前不在周期内的元素个数的最大值 周期长度的最小公倍数。
- 若不考虑 , 的数列,我们应当只会选择周期为 或 的数列,显然有一种从大到小贪心的做法,即优先选取 ,若当前 中以及有 因子,则选取 。
- 考虑 , 的数列,在上述贪心中,若存在一个数列,去掉它, 保持不变,则答案应当 。可以证明,上述贪心中被当做周期为 的数列依然只需要被当做周期为 的数列考虑,周期为 的数列依然只需要被当做周期为 的数列考虑。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e5 + 5; const int MAXV = 2e6 + 5; const int P = 1e9 + 7; 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, tot, prime[MAXV], f[MAXV]; int a[MAXN], Max[MAXV], cnt[MAXV]; bool type[MAXN]; void init(int n) { for (int i = 2; i <= n; i++) { if (f[i] == 0) prime[++tot] = f[i] = i; for (int j = 1; j <= tot && prime[j] <= f[i]; j++) { int tmp = prime[j] * i; if (tmp > n) break; f[tmp] = prime[j]; } } } int main() { read(n); for (int i = 1; i <= n; i++) read(a[i]); init(2e6); sort(a + 1, a + n + 1); reverse(a + 1, a + n + 1); for (int i = 1; i <= n; i++) { if (Max[a[i]]) { type[i] = true; int tmp = a[i] - 1; while (tmp != 1) { int fac = f[tmp], now = 0; while (tmp % fac == 0) { tmp /= fac; now++; } if (now > Max[fac]) { Max[fac] = now; cnt[fac] = 1; } else cnt[fac] += Max[fac] == now; } } else Max[a[i]] = cnt[a[i]] = 1; } int ans = 1; for (int i = 1; i <= 2e6; i++) for (int j = 1; j <= Max[i]; j++) ans = 1ll * ans * i % P; for (int i = 1; i <= n; i++) { bool found = false; if (type[i]) { found = true; int tmp = a[i] - 1; while (tmp != 1) { int fac = f[tmp], now = 0; while (tmp % fac == 0) { tmp /= fac; now++; } found &= Max[fac] > now || cnt[fac] >= 2; } } else found = Max[a[i]] >= 2 || cnt[a[i]] >= 2; if (found) { writeln((ans + 1) % P); return 0; } } writeln(ans); return 0; }
**【E】**Euler tour
【思路要点】
- 一个合法的欧拉序应当满足 ,不存在 使得 ,并且若 ,那么 是偶数。
- 对于满足上述几点的输入,我们可以用下面的算法构造出一组解。
- 、若 ,递归处理区间 ,然后将区间 缩成一个点。
- 、若不出现重复数的区间 元素个数为 ,其中非零元素个数应当不超过 ,否则问题无解。将非零元素个数补足 个,则有以下两种情况:
、存在三个连续的位置 ,将 赋为 ,然后将这三个数看做一个 。
、存在三个连续的位置 ,将 赋为 ,然后将这三个数看做一个 。
、不存在上述连续位置,则序列形如 ,在所有序列中的 处填入该子树的根部即可。- 时间复杂度 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 1e6 + 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 a[MAXN], nxtoccur[MAXN]; int n, pre[MAXN], suf[MAXN]; int now, vis[MAXN]; void error() { printf("no\n"); exit(0); } int getnum() { while (vis[now]) now++; if (now > n) error(); vis[now] = 1; return now; } void solve(int l, int r) { if ((r - l) % 2 != 0) error(); int start = pre[l], endpos = suf[r]; for (int i = suf[start]; i <= r; i = nxtoccur[i] ? nxtoccur[i] : suf[i]) { if (nxtoccur[i]) { int tmp = nxtoccur[i]; if (tmp > r) error(); solve(suf[i], pre[tmp]); int tnp = pre[i]; suf[tnp] = tmp, pre[tmp] = tnp; } } int cnt = 0, all = 0; for (int i = suf[start]; i <= r; i = suf[i]) all++, cnt += a[i] != 0; int goal = (all + 1) / 2; if (cnt > goal) error(); for (int i = suf[suf[start]]; i <= r && cnt < goal; i = suf[i]) { if (a[i] == 0) { cnt++; a[i] = getnum(); } } if (all != 1) { for (int i = suf[start], j = suf[suf[start]]; j <= r; i = suf[i], j = suf[j]) { while (i > suf[start] && a[i] && a[j]) { int tmp = pre[i]; if (a[tmp] == 0) { a[tmp] = a[j]; int tnp = pre[tmp]; suf[tnp] = j, pre[j] = tnp; i = tnp; } else break; } while (j < r && a[i] && a[j]) { int tmp = suf[j]; if (a[tmp] == 0) { a[tmp] = a[i]; int tnp = suf[tmp]; suf[i] = tnp, pre[tnp] = i; if (pre[i] >= l) j = i, i = pre[i]; else j = tnp; } else break; } } for (int i = suf[start]; i <= r; i = suf[i]) if (a[i] == 0) a[i] = a[start]; } else if (cnt == 0) a[suf[start]] = getnum(); endpos = pre[endpos]; suf[start] = endpos; pre[endpos] = start; } int main() { read(n); for (int i = 1; i <= 2 * n - 1; i++) { read(a[i]); suf[i] = i + 1; pre[i] = i - 1; } if (a[1] && a[2 * n - 1] && a[1] != a[2 * n - 1]) error(); if (a[1] == 0 || a[2 * n - 1] == 0) a[1] = a[2 * n - 1] = a[1] + a[2 * n - 1]; vis[0] = 1; for (int i = 2 * n - 1; i >= 1; i--) { if (a[i]) { if (vis[a[i]]) nxtoccur[i] = vis[a[i]]; vis[a[i]] = i; } } suf[0] = 1, pre[2 * n] = 2 * n - 1; solve(1, 2 * n - 1); printf("yes\n"); for (int i = 1; i <= 2 * n - 1; i++) printf("%d ", a[i]); return 0; }