NOI2025 模拟赛 R1~R2 笔记

NOI2025 模拟赛 R1~R2 笔记

[NOI2025 模拟赛 R1] A - 基环

首先有一个定理:

Cayley 定理
若有 \(n\) 个点组成了 \(k\) 个连通块,则添加 \(k-1\) 条边使其连通的方案数为 \(n^{k-2}\prod_{i=1}^{k} sz_i\)

有了这个定理,则当原图中有了一个环的时候就可以直接计算答案。

那么原图没有环的情况下就需要枚举环。

首先只需记录每个连通块的大小,接下来要从中选出一些块组成环。

若选出了 \(c\) 个块,那么计算这些块拼成环的方案:

\(c=1\),只需要在块内再加条边即可,方案数为 \(\binom{sz}{2}-sz+1\)

\(c=2\),则只需在两个块内连两条边,方案数为 \(\binom{sz_1sz_2}{2}\)

\(c=3\),首先制定环的顺序,需要考虑旋转和反转,方案为 \(\frac{(c-1)!}{2}\),然后相邻块之间可以连 \(sz_l sz_r\) 种边,所以总方案数为 \(\frac{(c-1)!}{2}\prod_{i=1}^{c}sz_i^2\)

随后使用 Cayley 定理还需要知道 \(\sum_{i=1}^{c}sz_i\)

考虑把 \(c\)\(\sum_{i=1}^{c}sz_i\) 记为状态,\(\prod_{i=1}^{c}sz_i\) 直接放进 DP 值,可以做到 \(O(n^3)\)

但是可以考虑再设一个 DP,同时把 \(\prod_{i=1}^{c}sz_i\sum_{i=1}^{c}sz_i\) 放进 DP 值,通过上面只记 \(\prod_{i=1}^{c} sz_i\) 的 DP 进行转移,就能做到 \(O(n^2)\)

ケロシの代码
const int N = 5e3 + 5;
const int P = 1e9 + 7;
inline int add(int x, int y) { return x + y < P ? x + y : x + y - P; }
inline void Add(int & x, int y) { x = x + y < P ? x + y : x + y - P; }
inline int sub(int x, int y) { return x < y ? x - y + P : x - y;  }
inline void Sub(int & x, int y) { x = x < y ? x - y + P : x - y;  }
inline int mul(int x, int y) { return (1ll * x * y) % P; }
inline void Mul(int & x, int y) { x = (1ll * x * y) % P; }
inline int mul(initializer_list<int> a) {
	int res = 1;
	for(int x : a) Mul(res, x);
	return res;
}
int fp(int x, int y) {
	int res = 1;
	for(; y; y >>= 1) {
		if(y & 1) Mul(res, x);
		Mul(x, x);
	}
	return res;
}
int n, m, f[N], sz[N];
int find(int u) {
	if(f[u] == u) return u;
	return f[u] = find(f[u]);
}
int pw[N], fac[N], inv[N];
int a[N], tot;
int dp[N][2];
inline int C2(int x) {
	return (1ll * x * (x - 1) / 2) % P; 
}
inline int pwn(int x) {
	return x == - 1 ? inv[n] : pw[x];
}
void solve() {
	cin >> n >> m;
	fac[0] = pw[0] = 1;
	FOR(i, 1, n) pw[i] = mul(pw[i - 1], n);
	FOR(i, 1, n) fac[i] = mul(fac[i - 1], i);
	FOR(i, 1, n) inv[i] = fp(i, P - 2);
	bool ok = 0;
	FOR(i, 1, n) f[i] = i, sz[i] = 1;
	REP(_, m) {
		int u, v;
		cin >> u >> v;
		u = find(u); v = find(v);
		if(u == v) ok = 1;
		else sz[u] += sz[v], f[v] = u;
	}
	if(ok) {
		int ans = 1, cnt = 0;
		FOR(i, 1, n) if(find(i) == i) Mul(ans, sz[i]), cnt ++;
		Mul(ans, pwn(cnt - 2));
		cout << ans << endl;
		return;
	}
	int sum = 1, ans = 0;
	FOR(i, 1, n) if(find(i) == i) a[++ tot] = sz[i];
	FOR(i, 1, tot) Mul(sum, a[i]);
	FOR(i, 1, tot) if(a[i] > 2) Add(ans, mul({
		C2(a[i]) - a[i] + 1,
		sum, pwn(tot - 2)
	}));
	if(tot == 2) {
		Add(ans, C2(a[1] * a[2]));
	}
	if(tot >= 3) {
		FOR(i, 1, tot) FOR(j, i + 1, tot) Add(ans, mul({
			sum, inv[a[i]], inv[a[j]], a[i] + a[j],
			C2(a[i] * a[j]), pwn(tot - 3)
		}));
	}
	dp[0][0] = 1;
	FOR(i, 1, tot) ROF(j, i, 1) {
		Add(dp[j][0], mul(dp[j - 1][0], a[i]));
		Add(dp[j][1], add(mul(dp[j - 1][1], a[i]), mul(dp[j - 1][0], a[i] * a[i])));
	}
	FOR(i, 3, tot) Add(ans, mul({
		dp[i][1], pwn(tot - i - 1), sum,
		fac[i - 1], P + 1 >> 1,
	}));
	cout << ans << endl;
}

[NOI2025 模拟赛 R1] B - 回滚

ケロシの代码
const int N = 3e5 + 5;
int n, m;
vector<array<int, 3>> e[N];
vector<int> q[N];
ll ans[N];
struct Node {
	int L, R; ll X;
};
struct SgT {
	int le[N << 2], ri[N << 2];
	Node F[N << 2];
	ll get(int u, int k) {
		if(k <= 0) return F[u].X;
		if(k >= F[u].R) return 0;
		if(k <= F[u << 1 | 1].R) return F[u].X - F[u << 1 | 1].X + get(u << 1 | 1, k);
		else return get(u << 1, k - F[u << 1 | 1].R + F[u << 1 | 1].L);
	}
	Node pushup(int L, Node R) {
		if(F[L].R <= R.L) {
			return {F[L].L + R.L - F[L].R, R.R, R.X};
		}
		else {
			ll val = get(L, R.L) + R.X;
			return {F[L].L, F[L].R - R.L + R.R, val};
		}
	}
	void build(int u, int l, int r) {
		le[u] = l, ri[u] = r;
		if(l == r) {
			return;
		}
		int mid = l + r >> 1;
		build(u << 1, l, mid);
		build(u << 1 | 1, mid + 1, r);
	}
	void insert(int u, int p, int o, int x) {
		if(le[u] == ri[u]) {
			if(o == 0) F[u] = {0, 0, 0};
			if(o == 1) F[u] = {0, 1, x};
			if(o == 2) F[u] = {1, 0, 0};
			return;
		}
		int mid = le[u] + ri[u] >> 1;
		if(p <= mid) insert(u << 1, p, o, x);
		else insert(u << 1 | 1, p, o, x);
		F[u] = pushup(u << 1, F[u << 1 | 1]);
	}
	Node query(int u, int r) {
		if(ri[u] <= r) {
			return F[u];
		}
		int mid = le[u] + ri[u] >> 1;
		if(r <= mid) return query(u << 1, r);
		return pushup(u << 1, query(u << 1 | 1, r));
	}
} t;
void solve() {
	cin >> n >> m;
	FOR(i, 1, m) {
		int o; cin >> o;
		if(o == 1) {
			int l, r, x;
			cin >> l >> r >> x;
			e[l].push_back({i, 1, x});
			e[r + 1].push_back({i, 0, 0});
		}
		if(o == 2) {
			int l, r;
			cin >> l >> r;
			e[l].push_back({i, 2, 0});
			e[r + 1].push_back({i, 0, 0});
		}
		if(o == 3) {
			int u; cin >> u;
			q[u].push_back(i);
		}
	}
	FOR(i, 1, m) ans[i] = - 1;
	t.build(1, 1, m);
	FOR(i, 1, n) {
		for(auto h : e[i]) 
			t.insert(1, h[0], h[1], h[2]);
		for(int u : q[i])
			ans[u] = t.query(1, u).X;
	}
	FOR(i, 1, m) if(ans[i] != - 1) cout << ans[i] << endl;
}

[NOI2025 模拟赛 R2] A - 下雨

ケロシの代码
const int N = 2e5;
const int P = 998244353;
inline int add(int x, int y) { return x + y < P ? x + y : x + y - P; }
inline void Add(int & x, int y) { x = x + y < P ? x + y : x + y - P; }
inline int sub(int x, int y) { return x < y ? x - y + P : x - y; }
inline void Sub(int & x, int y) { x = x < y ? x - y + P : x - y; }
inline int mul(int x, int y) { return (1ll * x * y) % P; }
inline void Mul(int & x, int y) { x = (1ll * x * y) % P; }
int fac[N]; ll f[N]; int F[N];
void init(int n) {
	fac[0] = 1;
	FOR(i, 1, n) fac[i] = mul(fac[i - 1], i);
	FOR(i, 1, n) f[i] = f[i - 1] + 1ll * i * i;
	FOR(i, 1, n) F[i] = add(F[i - 1], mul(i, fac[i]));
}
inline ll up(ll x, ll y) {
	return (x + y - 1) / y;
}
void solve() {
	ll n; cin >> n; n ++;
	int L = 1, R = N - 1, pos = 1;
	while(L <= R) {
		int mid = L + R >> 1;
		if(f[mid] >= n) {
			pos = mid;
			R = mid - 1;
		}
		else {
			L = mid + 1;
		}
	}
	int ans = 0;
	ll y = up(n - f[pos - 1], pos);
	n -= y * pos;
	Add(ans, mul(y, fac[pos]));
	pos --;
	if(n == f[pos]) {
		Add(ans, F[pos]);
	}
	else {
		int val = f[pos] - n;
		Add(ans, F[pos]);
		Sub(ans, fac[val]);
	}
	cout << ans << endl;
}

[NOI2025 模拟赛 R2] B - 沙尘暴

ケロシの代码
const int N = 5e2 + 5;
int I, n, C;
int h[N], d[N];
ll w[N][N], F[N][N];
ll dis[N], e[N][N], st[N][N], ed[N][N];
ll ans[N][N];
bool vis[N];
void solve() {
	cin >> I >> n >> C;
	FOR(i, 1, n) cin >> h[i];
	FOR(i, 1, n) cin >> d[i];
	FOR(i, 1, n) FOR(j, 1, n) cin >> w[i][j];
	memset(ans, 0x3f, sizeof ans);
	FOR(i, 1, n) FOR(j, 1, n) {
		ll val = w[i][j] + 1ll * abs(h[i] - h[j]) * C;
		e[i][j] = st[i][j] = ed[i][j] = val;
	}
	FOR(u, 1, n) {
		memset(dis, 0x3f, sizeof dis);
		memset(vis, 0, sizeof vis);
		dis[u] = 0;
		int cur = u;
		while(1) {
			int u = - 1;
			FOR(i, 1, n) if(! vis[i]) 
				if(u == - 1 || dis[u] > dis[i])
					u = i;
			if(u == - 1) break;
			vis[u] = 1;
			FOR(v, 1, n) {
				ll wt = w[u][v] + 1ll * abs(h[cur] - h[v]) * d[v];
				if(dis[v] > dis[u] + wt) {
					dis[v] = dis[u] + wt;
				}
			}
		}
		FOR(i, 1, n) F[u][i] = dis[i];
		FOR(i, 1, n) {
			chmin(st[i][u], dis[i]);
			chmin(ed[u][i], dis[i]);
		}
	}
	FOR(l, 1, n) FOR(r, 1, n) FOR(i, 1, n) {
		ll val = F[l][i] + F[r][i];
		ll L = 1ll * abs(h[i] - h[l]) * d[i];
		ll R = 1ll * abs(h[i] - h[r]) * d[i];
		val -= max(L, R);
		val += 1ll * abs(h[l] - h[r]) * C;
		chmin(e[l][r], val);
	}
	FOR(k, 1, n) FOR(i, 1, n) FOR(j, 1, n)
		chmin(e[i][j], e[i][k] + e[k][j]);
	FOR(k, 1, n) FOR(i, 1, n) FOR(j, 1, n)
		chmin(st[i][j], st[i][k] + e[k][j]);
	FOR(k, 1, n) FOR(i, 1, n) FOR(j, 1, n)
		chmin(ans[i][j], st[i][k] + ed[k][j]);
	FOR(i, 1, n) {
		FOR(j, 1, n) cout << ans[i][j] << " ";
		cout << endl;
	}
}

[NOI2025 模拟赛 R2] C - 宇宙射线

ケロシの代码
const int N = 2.5e7 + 5;
const int P = 998244353;
inline int add(int x, int y) { return x + y < P ? x + y : x + y - P; }
inline void Add(int & x, int y) { x = x + y < P ? x + y : x + y - P; }
inline int sub(int x, int y) { return x < y ? x - y + P : x - y; }
inline void Sub(int & x, int y) { x = x < y ? x - y + P : x - y; }
inline int mul(int x, int y) { return (1ll * x * y) % P; }
inline void Mul(int & x, int y) { x = (1ll * x * y) % P; }
int fp(int x, int y) {
	int res = 1;
	for(; y; y >>= 1) {
		if(y & 1) Mul(res, x);
		Mul(x, x);
	}
	return res;
}
int n, k;
int fac[N], finv[N];
int F[N * 3][3], G[N];
int p[N / 10], f[N], cnt;
bitset<N> ip;
void init(int n) {
	fac[0] = 1;
	FOR(i, 1, n) fac[i] = mul(fac[i - 1], i);
	finv[n] = fp(fac[n], P - 2);
	ROF(i, n - 1, 0) finv[i] = mul(finv[i + 1], i + 1);
	ip[1] = f[1] = 1;
	FOR(i, 2, n) {
		if(! ip[i]) {
			p[++ cnt] = i;
			f[i] = fp(i, k);
		}
		for(int j = 1; j <= cnt && p[j] * i <= n; j ++) {
			ip[p[j] * i] = 1;
			f[i * p[j]] = mul(f[i], f[p[j]]);
			if(i % p[j] == 0) break;
		}
	}
}
int C(int x, int y) {
	return mul(mul(fac[x], finv[y]), finv[x - y]);
}
inline int get(int m, int d) {
	return (1ll * F[d][0] * (m + 1)
		+ F[d - 1][2] - F[d - m - 1][2]
		+ 1ll * (F[d - m - 1][1] - F[d - 1][1]) * (d - m - 1)
		- F[d + m][2] + F[d][2] 
		+ 1ll * (F[d + m][1] - F[d][1]) * (d + m + 1)
	) % P;
}
void solve() {
	cin >> n >> k;
	init(n);
	FOR(i, 0, n) if(! ((n + i) & 1)) F[i][0] = C(n, n + i >> 1);
	FOR(i, 1, n) F[i][1] = add(F[i - 1][1], F[i][0]);
	FOR(i, 1, n) F[i][2] = add(F[i - 1][2], mul(F[i][0], i));
	FOR(i, n + 1, n * 3) F[i][1] = F[i - 1][1];
	FOR(i, n + 1, n * 3) F[i][2] = F[i - 1][2];
	int ans = 0;
	G[1] = 2;
	FOR(i, 2, n) {
		ll val = 0;
		val += 1ll * F[0][0] * (i + 1);
		val -= 1ll * F[i + 1][2] * 2;
		val += 1ll * F[i + 1][1] * (i * 2 + 2);
		int d = i + 2, o = 1;
		while(d <= n + i) {
			o ? val -= get(i, d) << 1 : val += get(i, d) << 1;
			d += i + 2; o ^= 1;
		}
		G[i] = ((val % P) + P) % P;
	}
	ROF(i, n, 1) Sub(G[i], G[i - 1]);
	FOR(i, 1, n) Add(ans, mul(f[i], sub(G[i], G[i - 1])));
	cout << ans << endl;
}
posted @ 2025-05-24 19:46  KevinLikesCoding  阅读(102)  评论(0)    收藏  举报