Loading

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;
}


posted @ 2025-07-15 12:27  qkhm  阅读(15)  评论(0)    收藏  举报