AtCoder Grand Contest 003 CDEF
C - BBuBBBlesort!
翻转相邻三个相当于交换 \(a_i,a_{i+2}\)。离散化把值域弄到 \([1,n]\),用尽量少的操作一使奇数在奇数位,偶数在偶数位,然后使用操作二一定可以让序列归位
每次操作一都可以让两个数奇偶归位,因为一定可以先通过操作二使一奇一偶相邻,然后让操作一产生 \(2\) 的贡献。统计一下 \(/2\) 就是答案
#include <bits/stdc++.h>
using namespace std;
void read (int &x) {
char ch = getchar(); x = 0; while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
} const int N = 1e5 + 5;
int n, a[N], b[N];
signed main() {
read (n);
for (int i = 1; i <= n; ++i) read (a[i]), b[i] = a[i];
sort (b + 1, b + n + 1);
for (int i = 1; i <= n; ++i)
a[i] = lower_bound (b + 1, b + n + 1, a[i]) - b;
int tmp = 0;
for (int i = 1; i <= n; ++i)
if ((a[i] + i) & 1) ++tmp;
cout << tmp / 2 << endl;
return 0;
}
D - Anticube
分类题。如果按照常规方法 \(\sqrt{A_i}\) 分解质因数大概率会 T,在 \(\sqrt[3]{A_i}\) 的范围内分解质因数成了不错的选择,复杂度不会过高,情况也不会很复杂。大致可以把数分为 \(4\) 类:
1、形如 \(x^3\),这样的数只能选一个
2、分解质因数后剩下的数为 \(tmp\),\(tmp= x\)
3、\(tmp = x^2\)
4、\(tmp = 1\)
第二类只能和第三类配对,第四类内部配对。对于两个矛盾的数,选个数较多的。\(map\) 搞一搞
#include <bits/stdc++.h>
using namespace std;
#define int long long
void read (int &x) {
char ch = getchar(); x = 0; while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
} const int N = 1e5 + 5, M = 1e10;
int n, mx, a[N], m, p[N], k[N];
void getprime () {
for (int i = 2; i <= mx; ++i) {
if (!k[i]) p[++m] = i;
for (int j = 1; j <= m && p[j] * i <= mx; ++j) {
k[i * p[j]] = 1; if (i % p[j] == 0) break;
}
}
}
unordered_map<int, int> is, one, cnt, pp, qq; int res = 0, e = 0;
#define fi first
#define se second
signed main() {
read (n);
for (int i = 1; i <= n; ++i) read (a[i]);
while (mx * mx * mx <= M) ++mx;
getprime ();
// for (int i = 1; i <= m; ++i) printf ("%lld\n", p[i]);
for (int i = 1; i <= mx; ++i) is[i * i * i] = 1;
for (int i = 1; i <= n; ++i) {
if (is[a[i]]) { e |= 1; continue; }
int tmp = 1, qwq = 1, now = a[i], t, tag = 0;
for (int j = 1; j <= m && p[j] <= now; ++j) {
t = 0;
while (now % p[j] == 0) now /= p[j], ++t; t %= 3;
for (int q = 1; q <= t; ++q) qwq *= p[j];
t = (3 - t) % 3;
for (int q = 1; q <= t; ++q) {
tmp *= p[j]; if (tmp > M) { tag = 1; break; }
}
if (tag) break;
}
if (tag) ++res;
else {
if (now == 1) ++one[tmp], ++cnt[qwq];
else {
int sq = sqrt (now);
if (sq * sq == now) ++pp[tmp * sq];
else ++qq[qwq * now];
}
}
}
int s = 0, ss = 0;
for (auto i : one) {
if (i.se > cnt[i.fi]) s += i.se;
if (i.se == cnt[i.fi]) ss += i.se;
}
res += s + ss / 2;
for (auto i : pp) if (i.se >= qq[i.fi]) res += i.se;
for (auto i : qq) if (i.se > pp[i.fi]) res += i.se;
printf ("%lld\n", res + e);
return 0;
}
E - Sequential operations on Sequence
一串下降的数只有最后一次有用,用单调栈维护序列 \(a\) 单调上升。之后的做法就比较神奇了
第 \(i\) 次形成的数组是 \(i-1\) 次的复制 \(\frac{a_i}{a_{i-1}}\) (取下整)遍加上后面一小段获得的。很可怜的是只有信息缩小到第一段才能快速计算答案。那就重复除法取模的过程,直到效果落到第一段上,这时可以差分处理。对于不在第一段的,记录一下被弄了几次,统计递归。可以看出这个过程要从后往前处理
#include <bits/stdc++.h>
using namespace std;
#define int long long
void read (int &x) {
char ch = getchar(); x = 0; while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
} const int N = 1e5 + 5;
int n, m, q, a[N], f[N], d[N];
void work (int s, int c) {
if (!s) return;
int w = upper_bound (a + 1, a + m + 1, s) - a - 1;
if (!w) d[1] += c, d[s + 1] -= c;
else f[w] += s / a[w] * c, work (s % a[w], c);
}
signed main() {
read (n), read (q);
a[++m] = n;
for (int i = 1, x; i <= q; ++i) {
read (x);
while (x < a[m] && m) --m;
a[++m] = x;
}
f[m] = 1;
for (int i = m; i > 1; --i)
f[i - 1] += a[i] / a[i - 1] * f[i], work (a[i] % a[i - 1], f[i]);
d[1] += f[1], d[a[1] + 1] -= f[1];
for (int i = 1; i <= n; ++i) printf ("%lld\n", d[i] += d[i - 1]);
return 0;
}
F - Fraction of Fractal
这个题看题解可能还是自己想来的快。
如果两个一级分形上下叠在一起,边界上能连通,左右叠在一起也可以,记为类型 \(1\)
如果只有左后或上下一种可以,记为类型 \(2\)
如果都不行,记为类型 \(3\)
对于类型 \(1\),无论怎样展开都是全部连通的,直接输出 \(1\)
对于类型 \(3\) ,答案就是 \(k-1\) 级分形中黑格的数量,即 \(cnt^{k-1}\)。因为最后一次展开后有 \(cnt^{k-1}\) 个一级分形,这些一级分形相互之间没有通道连通,单独成为一个连通块
\(2\) 是重点,以左右的情况为例(样例就是一个左右的类型 \(2\)),有了上面两种分析,可以发现答案是:\(k-1\) 级分形中黑格的数量减去左右相邻的黑格对数量。
只要求出左右相邻的黑格对数就 \(ok\) 了。
\(k\) 的范围很大,莫非这就是一个线性递推式的矩阵乘法优化?
猜对了!
先定义变量(和代码一致):\(ans\),当前左右相邻的黑格对数量;\(cnt\),一级分形中黑格数量;\(num’\),当前分形左右摆放后边界处相连的行数;\(tot\),一级分形中左右相邻黑格对数量;\(num\),一级分形左右摆放后边界处相连的行数。样例中 \(cnt=6,tot=2,num=2\)
有递推式 \(ans = ans \times cnt + num' \times tot, num' = num'\times num\)
可以这样理解:把 \(p\) 级分形按照一级分形划分成 \(h\times w\) 块,一级分形中白色的区域全都是白色,黑色的区域是一个 \(p-1\) 级分形。\(ans\times cnt\) 表示 \(cnt\) 个 \(p-1\) 级分形内部的答案,\(num'\times tot\) 表示相邻的 \(p-1\) 级分形左右边界上相邻的数量
#include <bits/stdc++.h>
using namespace std;
#define int long long
void read (int &x) {
char ch = getchar(); x = 0; while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
} const int N = 1005, mod = 1e9 + 7;
int n, m, k, cnt, tot, num; char a[N][N];
int qpow (int x, int y) {
int t = 1; while (y) { if (y & 1) t = t * x % mod; x = x * x % mod, y /= 2; } return t;
}
struct mat {
int a[2][2];
void clear () { memset (a, 0, sizeof (a)); }
mat operator * (const mat &b) const {
mat t; t.clear();
for (int i = 0; i < 2; ++i)
for (int j = 0; j < 2; ++j)
for (int k = 0; k < 2; ++k) (t.a[i][j] += a[i][k] * b.a[k][j]) %= mod;
return t;
}
} res, u;
signed main() {
read (n), read (m), read (k); --k;
for (int i = 1; i <= n; ++i) {
scanf ("%s", a[i] + 1);
for (int j = 1; j <= m; ++j) a[i][j] = (a[i][j] == '#'), cnt += a[i][j];
}
int ka = 0, kb = 0;
for (int i = 1; i <= n; ++i) ka |= (a[i][1] & a[i][m]);
for (int i = 1; i <= m; ++i) kb |= (a[1][i] & a[n][i]);
if (!ka && !kb) return printf ("%lld\n", qpow (cnt, k)), 0;
if (ka && kb) return puts ("1"), 0;
// ans = ans * cnt + num' * tot, num' = num' * num
// cnt,一级分形中黑格数量;num’,当前分形左右摆放后边界处相连的行数;tot,一级分形中左右相邻黑格对数量;num,一级分形左右摆放后边界处相连的行数
if (ka) {
for (int i = 1; i <= n; ++i)
for (int j = 1; j < m; ++j) tot += (a[i][j] && a[i][j + 1]);
for (int i = 1; i <= n; ++i) num += (a[i][1] && a[i][m]);
} else {
for (int j = 1; j <= m; ++j)
for (int i = 1; i < n; ++i) tot += (a[i][j] && a[i + 1][j]);
for (int i = 1; i <= m; ++i) num += (a[1][i] && a[n][i]);
}
int ans = qpow (cnt, k);
res.a[0][1] = 1, u.a[0][0] = cnt, u.a[1][0] = tot, u.a[1][1] = num;
while (k) { if (k & 1) res = res * u; u = u * u, k >>= 1; }
return printf ("%lld\n", (ans += mod - res.a[0][0]) % mod), 0;
}