MX galaxy Day1
juice
除了初始状态外,每个状态至少有一个杯子是空或者满的,所以总状态数是 \(O(C)\) 的。
seg
猫树分治模板。
\(\boldsymbol{记得算空间}\) 。
gcd
转换成一个贪心,设区间 \(gcd = g\) 。
则从左端点开始,向右取 \(gcd\) ,只要当前 \(gcd = g\) ,立刻停止,记录答案,从下一位继续重复这个过程,直到右端点。
输出总区间长度 - 记录的答案。
现在我们想要加速这个过程,首先,我们只关注从 \(L\) 开始向右取 \(gcd\) ,发生改变的位置,而这样的位置只有 \(log\) 个。
预处理出每个点 \(i\) 出发,取出 \(g\) 的第一个点的下一位是 \(nxt[i, g]\) 。
可以通过 \(ST\) 上二分来处理 \(nxt\) 。
具体地,将 \(i\) 与当前位置连边,然后跳过与当前 \(gcd\) 相同的(可以通过整除来判断)位置,重复。
但这样还是有问题的,如果区间完全相同就变成 \(O(n)\) 的了,所以我们要对边权相同的进行倍增,来快速处理这个部分。
点击查看
#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
const int _ = 1e5 + 7;
const int V = 16;
typedef long long ll;
int n, m, a[_], st[_][V + 1], lg[_];
std::map <int, int> to[_][V + 1];
int Gcd(int l, int r) {
int k = lg[r - l + 1];
return std::__gcd(st[l][k], st[r - (1 << k) + 1][k]);
}
void Add(int u, int v, int g) {
to[u][0][g] = v;
lep(i, 1, V) if (to[u][i - 1].count(g) and to[to[u][i - 1][g]][i - 1].count(g))
to[u][i][g] = to[to[u][i - 1][g]][i - 1][g];
}
void Init() {
lep(i, 2, n) lg[i] = lg[i >> 1] + 1;
lep(j, 1, V) lep(i, 1, n - (1 << j) + 1)
st[i][j] = std::__gcd(st[i][j - 1], st[i + (1 << j - 1)][j - 1]);
rep(i, n, 1) {
int g = a[i], p = i;
while (true) {
if (p > n) break; g = std::__gcd(g, a[p]), Add(i, p + 1, g);
rep(j, V, 0) if (p + (1 << j) - 1 <= n and st[p][j] % g == 0) p += (1 << j);
}
}
}
int main() {
#ifndef DEBUG
freopen("gcd.in", "r", stdin);
freopen("gcd.out","w",stdout);
#endif
scanf("%d%d", & n, & m);
lep(i, 1, n) scanf("%d", a + i), st[i][0] = a[i];
Init();
int l, r;
lep(i, 1, m) {
scanf("%d%d", & l, & r); int g = Gcd(l, r);
int p = l, ans = 0;
rep(j, V, 0) if (to[p][j][g] and to[p][j][g] <= r + 1)
p = to[p][j][g], ans += (1 << j);
printf("%d\n", r - l + 1 - ans);
}
return 0;
}
permutation
见到冒泡排序,考虑 trick 。
设 \(x_i = \sum_{j<i} p_j > p_i\) ,每次冒泡排序相当于每个 \(>0\) 的 \(x_i\) 自减 \(1\) 。
而这样的 \(x\) 与 \(p\) 是构成双射的,容易发现可以从后往前解密。
考虑答案是什么样的,对于一个序列,答案为 \(\sum \max\{0, x_i-x_{i-1} \}\) 。
\(\boldsymbol{拆贡献}\) ,考虑什么样的 \(j < i\) 会让 \(x_i\) \(+1\) ,而 \(x_{i-1}\) 不会。
一定满足 \(p_i < p_j \le p_{i-1} \wedge j<i\) 。
然后问题就变成数这样的点对。
分四种情况,\(p_i\) 是否确定和 \(p_{i-1}\) 是否确定。
分类讨论,用树状数组计数即可。
注意除了我们考虑的部分外,剩下的 \(?\) 位置可以任意排。
点击查看
#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
const int _ = 2e6 + 7;
const int mod = 998244353;
typedef long long ll;
int n, m, p[_]; ll ans, cnt, fac[_], res;
struct BIT {
ll c[_];
void Add(int x, ll k) { while (x <= n) (c[x] += k) %= mod, x += x & -x; }
ll Query(int x) { ll res = 0; while (x) res = (res + c[x]) % mod, x -= x & -x; return res; }
ll Query(int l, int r) { return Query(r) - Query(l - 1); }
}V, A, C, D;
//V avalable num
//A pre confirmed
//C
//D
ll C2(ll n) { return n * (n - 1) / 2 % mod; }
ll C3(ll n) { return n * (n - 1) * (n - 2) / 6 % mod; }
int main() {
#ifndef DEBUG
freopen("permutation.in", "r", stdin);
freopen("permutation.out","w",stdout);
#endif
scanf("%d%d", & n, & m); int x, y;
fac[0] = 1;
lep(i, 1, n) fac[i] = fac[i - 1] * i % mod, V.Add(i, 1);
lep(i, 1, m) scanf("%d%d", & x, & y), p[x] = y, V.Add(y, -1);
lep(i, 1, n) {
if (p[i]) A.Add(p[i], 1), C.Add(p[i], V.Query(p[i] + 1, n)), D.Add(p[i], V.Query(p[i])),
res = (res + V.Query(p[i]) * V.Query(p[i] + 1, n) % mod) % mod;
else ++cnt;
if (i == 1) continue;
if (p[i]) {
if (p[i - 1]) {
if (n - m and p[i] < p[i - 1]) ans = (ans + fac[n - m - 1] * V.Query(p[i] + 1, p[i - 1] - 1) % mod * cnt % mod) % mod;
if (p[i] < p[i - 1]) ans = (ans + fac[n - m] * A.Query(p[i] + 1, p[i - 1]) % mod) % mod;
}
else {
if (n - m) ans = (ans + fac[n - m - 1] * C.Query(p[i] + 1, n) % mod) % mod,
ans = (ans + fac[n - m - 1] * V.Query(p[i] + 1, n) % mod) % mod;
if (cnt > 1) ans = (ans + fac[n - m - 2] * (cnt - 1) % mod * C2(V.Query(p[i] + 1, n)) % mod) % mod;
}
}
else if (p[i - 1]) {
if (n - m) ans = (ans + fac[n - m - 1] * D.Query(p[i - 1]) % mod) % mod;
if (cnt > 1) ans = (ans + fac[n - m - 2] * (cnt - 1) % mod * C2(V.Query(p[i - 1])) % mod) % mod;
}
else {
if (n - m > 1) ans = (ans + res * fac[n - m - 2]) % mod,
ans = (ans + C2(n - m) * fac[n - m - 2] % mod) % mod;
if (cnt > 2) ans = (ans + fac[n - m - 3] * C3(n - m) % mod * (cnt - 2) % mod) % mod;
}
}
printf("%lld\n", ans);
return 0;
}
时间仓促,如有错误欢迎指出,欢迎在评论区讨论,如对您有帮助还请点个推荐、关注支持一下

如题
浙公网安备 33010602011771号