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

浙公网安备 33010602011771号