2017 多校联合训练 1 题解
Problem 1001
其实就是求$2^{m}$有几位
那么答案就是$m * \log_{2}10$
#include<bits/stdc++.h> using namespace std; int m; int ca = 0; int main(){ int ca = 0; while (~scanf("%d", &m)) printf("Case #%d: %d\n", ++ca, (int)(1.0 * (double)m * log(2) / log(10))); return 0; }
Problem 1002
这道题的关键就是求出每个字母的价值。
那么我们对所有出现过的字母进行排序,排在越前面的字母价值越大。
那么关键字是什么呢?
令a[ch][N]表示字母ch对整个答案的贡献。
当某个字符串的第x位(注意一开始所有字符串要反转)为ch时a[ch][x]加一。
因为这类似高精度加法的过程,所以我们需要考虑进位。
(比赛的时候就因为没考虑到进位WA了半天QAQ)
于是我们可以求出所有的a[ch]。然后按照a[ch]降序排序。
不过这题还有个特殊情况。。那就是那些长度大于1的字符串的首位价值不能为0。
这个地方要特判下。
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) typedef long long LL; const LL mod = 1e9 + 7; const int N = 1e5 + 20; const int A = 30; LL v[A], bits[N], ans; char st[N]; int a[A][N], can[A], e[A], f[A]; int n, len, cnt, ca = 0; inline int num(char ch){ return (int)ch - 96; } inline int chr(int num){ return (char)(num + 96);} bool cmp(int x, int y){ dec(i, N - 2, 0) if (a[x][i] ^ a[y][i]) return a[x][i] > a[y][i]; return true; } int main(){ bits[0] = 1LL; rep(i, 1, N - 2) bits[i] = bits[i - 1] * 26 % mod; while (~scanf("%d", &n)){ memset(a, 0, sizeof a); rep(i, 0, 27) can[i] = 1; rep(i, 0, 27) e[i] = 0; rep(i, 1, n){ scanf("%s", st); len = strlen(st); if (len > 1) can[num(st[0])] = 0; rep(j, 0, len / 2 - 1) swap(st[j], st[len - j - 1]); rep(j, 0, len - 1) e[num(st[j])] = 1; rep(j, 0, len - 1) ++a[num(st[j])][j]; } cnt = 0; rep(i, 0, 27) if (e[i]) f[++cnt] = i; rep(p, 1, 26){ rep(j, 0, N - 2) if (a[p][j] >= 26){ a[p][j + 1] += a[p][j] / 26; a[p][j] %= 26; } } sort(f + 1, f + cnt + 1, cmp); memset(v, 0, sizeof v); if (cnt ^ 26) rep(i, 1, cnt) v[f[i]] = 26 - i; else{ int u; dec(i, cnt, 1) if (can[f[i]]){ v[f[i]] = 0; u = f[i]; break; } int cal = 26; rep(i, 1, cnt) if (f[i] ^ u) v[f[i]] = --cal; } ans = 0; rep(i, 1, cnt) rep(j, 0, N - 2) (ans += bits[j] * v[f[i]] % mod * a[f[i]][j] % mod) %= mod; printf("Case #%d: %lld\n", ++ca, ans); } return 0; }
Problem 1003
比赛的时候想不到啊……
考虑树形DP。
设树上所有颜色数量为cnt。
如果所有路径上的颜色都是各不相同的,那么答案就是$C_{n}^{2} * cnt$
现在考虑减掉那些重复计算的颜色数。
对于某一种颜色,我们求出所有的不包含这个颜色的连通块。
若其中某个连通块大小为x,那么最后答案要减去$C_{x}^{2}$
那么我们减去每一种颜色的所有这样的连通块对答案的贡献就可以了。
当然这道题用树的点分治也是可以做的。
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) typedef long long LL; const int N = 2e5 + 10; int a[N], n, ca = 0; vector <int> v[N]; LL sz[N], sum[N], cnt, ans; void dfs(int x, int fa){ sz[x] = 1; LL ret = 0; for (auto u : v[x]){ if (u == fa) continue; LL now = sum[a[x]]; dfs(u, x); sz[x] += sz[u]; LL add = sum[a[x]] - now; ans += (sz[u] - add) * (sz[u] - add - 1) / 2; ret += sz[u] - add; } sum[a[x]] += ret + 1; } int main(){ while (~scanf("%d", &n)){ memset(sum, 0, sizeof sum); rep(i, 0, n + 1) v[i].clear(); set <int> s; s.clear(); rep(i, 1, n){ scanf("%d", a + i); s.insert(a[i]); } cnt = (LL)s.size(); rep(i, 2, n){ int x, y; scanf("%d%d", &x, &y); v[x].push_back(y); v[y].push_back(x); } ans = 0; dfs(1, 0); rep(i, 1, n){ if ((int)s.count(i) == 0) continue; ans += (LL)(n - sum[i]) * (n - sum[i] - 1) / 2; } printf("Case #%d: %lld\n", ++ca, (LL)n * (n - 1) / 2 * cnt - ans); } return 0; }
Problem 1006
我们先分别求出a[], b[]的循环节的长度,放在A[], B[]里。
然后对于每一个A[i],如果存在B[j]是他的因子,那么f[A[i]]加上B[j]。
最后把所有的f[A[i]]累乘即可。
#include<bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) typedef long long LL; const int N = 1e5 + 10; const LL mod = 1e9 + 7; LL c[N], f[N], ans; int A[N], B[N], a[N], b[N]; bool vis[N]; int n, m, cnt, tot, ca = 0; int main(){ while (~scanf("%d%d", &n, &m)){ cnt = 0, tot = 0; memset(vis, 0, sizeof vis); memset(c, 0, sizeof c); memset(f, 0, sizeof f); rep(i, 0, n - 1) scanf("%d", a + i); rep(i, 0, m - 1) scanf("%d", b + i); rep(i, 0, n - 1){ if(!vis[i]){ int tmp = 1, k = a[i]; vis[i] = 1; while (k ^ i) vis[k] = 1, k = a[k], tmp++; A[++tot] = tmp; } } memset(vis, 0, sizeof vis); rep(i, 0, m - 1){ if(!vis[i]){ int tmp = 1, k = b[i]; vis[i] = 1; while (k ^ i) vis[k] = 1, k = b[k], tmp++; B[++cnt]=tmp; } } rep(i, 1, cnt) (c[B[i]] += B[i]) %= mod; rep(i, 1, max(n, m)) for (int j = i; j <= max(n, m); j += i) (f[j] += c[i]) %= mod; ans = 1; rep(i, 1, tot) ans = ans * f[A[i]] % mod; printf("Case #%d: %lld\n", ++ca, ans); } return 0; }
Problem 1008
这个数组大小为$10^{7}$
直接sort肯定超时。
这道题可以用到一个函数nth_element(a, a + x, a + n)
他的意思是对于a数组(下标范围为0~n-1),我要找到其中第x小的元素,并把他放在a[x]这个位置。
并且满足操作之后,a[x]左边的数都小于等于a[x],a[x]右边的数都大于等于a[x]
但是不能保证这个数列是有序的
这道题我们就要用这个函数来减小计算量。
根据题目条件,我们发现这个b[]数组的增长速率大于等于斐波那契数列的增长速率。
于是我们倒着处理,不断把大于比当前这个数大的所有数挤到右边。
那么后续计算的时候这些数就不在统计范围内了。
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) #define MP make_pair #define fi first #define se second typedef long long LL; const int N = 1e7 + 10; const int M = 105; struct node{ int x; int id; unsigned int ans; friend bool operator < (const node &a, const node &b){ return a.x < b.x; } } q[M]; int n, m; unsigned int x, y, z; unsigned int a[N]; int ca = 0; unsigned rng61() { unsigned t; x ^= x << 16; x ^= x >> 5; x ^= x << 1; t = x; x = y; y = z; z = t ^ x ^ y; return z; } bool cmp(const node &a, const node &b){ return a.id < b.id; } int main(){ while (~scanf("%d%d%u%u%u", &n, &m, &x, &y, &z)){ rep(i, 1, m){ scanf("%d", &q[i].x); q[i].id = i; } sort(q + 1, q + m + 1); rep(i, 1, n) a[i] = rng61(); int pos = n; dec(i, m, 1){ int x = q[i].x; nth_element(a, a + x, a + pos); pos = x; q[i].ans = a[x]; } sort(q + 1, q + m + 1, cmp); printf("Case #%d:", ++ca); rep(i, 1, m) printf(" %u", q[i].ans); putchar(10); } return 0; }
Problem 1012
这道题题意十分难懂……
对我来说这题的难度就在于难以估算时间复杂度。
2s的时限,1e6的规模,以及题目对读入速度的要求,很难让我从O(nlogn)的角度去考虑。
简单描述这个过程,就是
令calc(L, R)为区间[L, R]的方案数。
然后在这个区间内找到被这个区间覆盖并且元素个数最多的区间编号x。
这个过程线段树维护即可。
有解的必要条件为l[x] = L, r[x] = R
然后这样一来区间就被划分成了[L, x - 1]和[x + 1, R]
去掉中间的x这个数,我们还有L - R个数可供选择。
要把这些数填到左边这个子区间(右边也可以)
那么答案就是calc(L, x - 1) * calc(x + 1, R) * C(R - L, x - L)
递归操作即可。
#include <bits/stdc++.h> namespace IO{ const int MT = 40 * 1024 * 1024; char IO_BUF[MT]; int IO_PTR, IO_SZ; void begin(){ IO_PTR = 0; IO_SZ = fread (IO_BUF, 1, MT, stdin); } template<typename T> inline bool scan_d (T & t){ while (IO_PTR < IO_SZ && IO_BUF[IO_PTR] != '-' && (IO_BUF[IO_PTR] < '0' || IO_BUF[IO_PTR] > '9'))IO_PTR ++; if (IO_PTR >= IO_SZ) return false; bool sgn = false; if (IO_BUF[IO_PTR] == '-') sgn = true, IO_PTR ++; for (t = 0; IO_PTR < IO_SZ && '0' <= IO_BUF[IO_PTR] && IO_BUF[IO_PTR] <= '9'; IO_PTR ++) t = t * 10 + IO_BUF[IO_PTR] - '0'; if (sgn) t = -t; return true; } }; using namespace IO; using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) #define lson i << 1, L, mid #define rson i << 1 | 1, mid + 1, R typedef long long LL; const LL mod = 1e9 + 7; const int N = 1e6 + 10; int l[N], r[N], a[N], mx[N << 2]; int n, ca = 0; LL fac[N], ans; inline LL Pow(LL a, LL b, LL mod){ LL ret(1); for (; b; b >>= 1, (a *= a) %= mod) if (b & 1) (ret *= a) %= mod; return ret;} inline LL C(LL n, LL m){ return m > n ? 0 : fac[n] * Pow(fac[m] * fac[n - m] % mod, mod - 2, mod) % mod; } inline int cmp(int x, int y){ return a[x] > a[y] ? x : y; } void build(int i, int L, int R){ if (L == R){ mx[i] = L; return; } int mid = (L + R) >> 1; build(lson); build(rson); mx[i] = cmp(mx[i << 1], mx[i << 1 | 1]); } int query(int i, int L, int R, int l, int r){ if (l <= L && R <= r) return mx[i]; int mid = (L + R) >> 1; if (r <= mid) return query(lson, l, r); else if (l > mid) return query(rson, l, r); else return cmp(query(lson, l, r), query(rson, l, r)); } LL calc(int L, int R){ if (L > R) return 1LL; int x = query(1, 1, n, L, R); if (l[x] != L || r[x] != R) return 0; if (L == R) return 1LL; return calc(L, x - 1) * calc(x + 1, R) % mod * C(R - L, x - L) % mod; } int main(){ fac[0] = 1LL; rep(i, 1, 1e6 + 2) fac[i] = fac[i - 1] * i % mod; IO::begin(); while (scan_d(n) == true){ rep(i, 1, n) scan_d(l[i]); rep(i, 1, n) scan_d(r[i]); rep(i, 1, n) a[i] = r[i] - l[i] + 1; build(1, 1, n); ans = calc(1, n); printf("Case #%d: %lld\n", ++ca, ans); } return 0; }