2026.9.14 考试总结
考试总结
估:100 + 100 + 100 + 100
实:100 + 100 + 100 + 100 rk1
水水 dp 场。
B Coding
改成了 13 位数且至少 4 位数相邻。
直接数位 dp 即可。
code
#include <bits/stdc++.h>
using namespace std;
#define int ll
#define ll long long
#define usd unsigned
#define el putchar('\n')
#define lowbit(x) (x & (-x))
#define AC return
#define AK return 0
#define YS cout << "YES"
#define NO cout << "NO"
#define Ys cout << "Yes"
#define No cout << "No"
#define ys cout << "yes"
#define no cout << "no"
#define ls(i) ch[i][0]
#define rs(i) ch[i][1]
#define debug(num) cerr << #num << ' ' << num << '\n'
#define void inline void
#define il inline
#define Mod(x) (((x) % mod + mod) % mod)
#define pii pair <int, int>
#define pll pair <ll, ll>
il char gc() { char c; while((c = getchar()) <= ' ') ; return c; }
il int rd() {
char c; int x, f = 1;
while(!isdigit(c = getchar())) if(c == '-') f *= -1;
x = c ^ 48;
while(isdigit(c = getchar())) x = (x << 3) + (x << 1) + (c ^ 48);
return x * f;
}
void ACehomoxue();
signed main() {
srand(time(0));
freopen("bcode.in", "r", stdin);
freopen("bcode.out", "w", stdout);
int t = 1;
// t = rd();
while(t--) ACehomoxue();
AK;
}
const int mod = 998244353, maxn = 20 + 18;
int dp[15][11][5][2][2][2], a[15];
il int dfs(int pos, int last, int cnt, bool ok, bool has8, bool has4, bool limit) {
if(has8 && has4) return 0;
if(pos == 13) return ok ? 1 : 0;
if(!limit && dp[pos][last][cnt][ok][has8][has4] != -1) {
return dp[pos][last][cnt][ok][has8][has4];
}
int up = limit ? a[pos] : 9, st = (pos == 0) ? 1 : 0, res = 0;
for(int d = st; d <= up; ++d) {
int c = (d == last) ? min(4ll, cnt + 1) : 1;
res += dfs(pos + 1, d, c, ok || (c >= 4), has8 || (d == 8), has4 || (d == 4), limit && (d == up));
}
if(!limit) dp[pos][last][cnt][ok][has8][has4] = res;
return res;
}
il int solve(int n) {
if(n < 1e12) return 0;
for(int i = 12; i >= 0; i--) {
a[i] = n % 10;
n /= 10;
}
return dfs(0, 10, 0, false, false, false, true);
}
int l, r;
void ACehomoxue() {
memset(dp, -1, sizeof(dp));
l = rd() - 1, r = rd();
cout << solve(r) - solve(l);
el;
}
Brute Force
滑动窗口。对于每个 \(l\) 找到最大的 \(r\) 使得 \([l, r]\) 合法,利用双指针单调队列可以均摊 \(O(n)\) 找到,那么 \([l, k] \land k \in [l, r]\) 都应合法,可以简单地算出合法区间个数,记为 \(cnt\),答案显然为 \((\frac {cnt} {\frac {n(n + 1)} 2}) ^ q\)。做完了。
code
#include <bits/stdc++.h>
using namespace std;
#define int int
#define ll long long
#define usd unsigned
#define el putchar('\n')
#define lowbit(x) (x & (-x))
#define AC return
#define AK return 0
#define YS cout << "YES"
#define NO cout << "NO"
#define Ys cout << "Yes"
#define No cout << "No"
#define ys cout << "yes"
#define no cout << "no"
#define ls(i) ch[i][0]
#define rs(i) ch[i][1]
#define debug(num) cerr << #num << ' ' << num << '\n'
#define void inline void
#define il inline
#define Mod(x) (((x) % mod + mod) % mod)
#define pii pair <int, int>
#define pll pair <ll, ll>
il char gc() { char c; while((c = getchar()) <= ' ') ; return c; }
il int rd() {
char c; int x, f = 1;
while(!isdigit(c = getchar())) if(c == '-') f *= -1;
x = c ^ 48;
while(isdigit(c = getchar())) x = (x << 3) + (x << 1) + (c ^ 48);
return x * f;
}
void ACehomoxue();
signed main() {
srand(time(0));
freopen("brute.in", "r", stdin);
freopen("brute.out", "w", stdout);
int t = 1;
// t = rd();
while(t--) ACehomoxue();
AK;
}
const int mod = 998244353, maxn = 4 * 1e7 + 6;
int n, q, d, type, a[maxn], ans;
namespace rdin {
void init() {
if(type == 0) {
for(int i = 1; i <= n; i++) {
a[i] = rd();
}
} else {
ll x = rd(), y = rd(), z = rd(), i1 = rd(), i2 = rd(), m = rd();
ll p1 = i2, p2 = i1, mask = (1LL << 30) - 1;
ll p, l, r, len;
for(int j = 1, lst = 0; j <= m; j++) {
p = rd(), l = rd(), r = rd(), len = r - l + 1;
for(int i = lst + 1; i <= p; i++) {
ll bi = 0;
if(i == 1) bi = i1;
else if(i == 2) bi = i2;
else {
bi = (x * p1 + y * p2 + z) & mask;
p2 = p1;
p1 = bi;
}
a[i] = (bi % len) + l;
}
lst = p;
}
}
}
}
il ll ksm(ll a, int k = mod - 2) { ll res = 1; for(; k; k >>= 1, a = a * a % mod) if(k & 1) res = res * a % mod; return res; }
int mx[maxn], lmx = 1, rmx = 0, mn[maxn], lmn = 1, rmn = 0;
#define qmx (lmx <= rmx ? a[mx[lmx]] : 0)
#define qmn (lmn <= rmn ? a[mn[lmn]] : (int) 1e9)
#define lstmx (lmx <= rmx ? a[mx[rmx]] : 0)
#define lstmn (lmn <= rmn ? a[mn[rmn]] : (int) 1e9)
void ACehomoxue() {
n = rd(), q = rd(), d = rd(), type = rd();
rdin :: init();
for(int i = 1, j = 0; i <= n; i++) {
while(lmx <= rmx && mx[lmx] < i) lmx++;
while(lmn <= rmn && mn[lmn] < i) lmn++;
while(j < n && max(qmx, a[j + 1]) - min(qmn, a[j + 1]) <= d) {
while(rmx >= lmx && lstmx <= a[j + 1]) rmx--;
mx[++rmx] = j + 1;
while(rmn >= lmn && lstmn >= a[j + 1]) rmn--;
mn[++rmn] = j + 1;
j++;
}
ans = ans + j - i + 1;
if(ans > mod) ans -= mod;
}
cout << ksm(1ll * ans * ksm(1ll * n * (n + 1) / 2 % mod) % mod, q);
el;
}
Domino
弱化版:P10975 Mondriaan's Dream / 蒙德里安的梦想
将原题的 \(h, w\) 改为了小于等于 \(20\)。这里提供一种题解没得的赛时做法,十分好写。
我们考虑枚举每一行并装压 dp,设状态 \(s\) 每一位表示是否被填过了,那么对于上一行是状态 \(s\),我们要转移到下一行 \(i\),只考虑竖着的块,那么为 \(0\) 的位都应该被填上一个竖着的块,第 \(i\) 行的这一位一定为 \(1\),对于第 \(i - 1\) 行为 \(1\) 的这一行只考虑竖着的贡献那么就应为 \(0\),总结一下只考虑竖着的贡献就是所有的位取反,即亦或一下 \(2 ^ m - 1\)。那么我们来填横着的,对于第 \(i\) 行原来状态中第 \(j\) 列和 \(j - 1\) 列都为 \(0\) 的显然可以填上一个横着的,那我们枚举 \(j\) 和状态 \(s\),类似完全背包的转移即可。时间复杂度 \(O(n ^ 2 2 ^ n)\)。做完了。
code
#include <bits/stdc++.h>
using namespace std;
#define int ll
#define ll long long
#define usd unsigned
#define el putchar('\n')
#define lowbit(x) (x & (-x))
#define AC return
#define AK return 0
#define YS cout << "YES"
#define NO cout << "NO"
#define Ys cout << "Yes"
#define No cout << "No"
#define ys cout << "yes"
#define no cout << "no"
#define ls(i) ch[i][0]
#define rs(i) ch[i][1]
#define debug(num) cerr << #num << ' ' << num << '\n'
#define void inline void
#define il inline
#define Mod(x) (((x) % mod + mod) % mod)
#define pii pair <int, int>
#define pll pair <ll, ll>
il char gc() { char c; while((c = getchar()) <= ' ') ; return c; }
il int rd() {
char c; int x, f = 1;
while(!isdigit(c = getchar())) if(c == '-') f *= -1;
x = c ^ 48;
while(isdigit(c = getchar())) x = (x << 3) + (x << 1) + (c ^ 48);
return x * f;
}
void ACehomoxue();
signed main() {
srand(time(0));
freopen("domino.in", "r", stdin);
freopen("domino.out", "w", stdout);
int t = 1;
// t = rd();
while(t--) ACehomoxue();
AK;
}
const int mod = 998244353, maxn = 24, maxs = (1 << 20) + 6;
int n, m, tot, dp[maxs];
void ACehomoxue() {
n = rd(), m = rd();
if(n > m) swap(n, m);
tot = (1 << m) - 1;
dp[tot] = 1;
for(int id = 1; id <= n; id++) {
for(int s = 0; s <= tot; s++) if(s < (s ^ tot)) swap(dp[s], dp[s ^ tot]);
for(int i = 2; i <= m; i++) {
for(int s = tot, t; s + 1; s--) {
if(((s >> i - 1) & 1) == 0 && ((s >> i - 2) & 1) == 0) {
t = (s | (1 << i - 1) | (1 << i - 2));
dp[t] += dp[s];
}
}
}
}
cout << dp[tot];
el;
}
Partition
我们设 \(dp_{i, j}\) 表示前 \(i\) 个分成 \(j\) 段的最小代价,我们考虑第一维枚举 \(j\) 然后枚举 \(i\)。我们考虑设 \(lst_{i, c}\) 表示 \(i\) 之前最后一个元素 \(c\) 出现的位置,\(nxt_i\) 表示下一次,那么转移可以写成:
\(\sum_c lst_{i, c}\) 可以动态维护。对于从 \(j\) 转移到 \(i\) 可能出现元素 \(c\) 没有出现于区间 \((k, i]\) 导致 \(nxt_{k, c} > lst_{i, c}\) 不能直接减。我们考虑动态维护 \(-\sum_c \min(nxt_{k, c}, lst_{i, c})\),这样直接减就合法,设 \(f_k = dp_{k, j - 1} -\sum_c \min(nxt_{j, c}, lst_{i, c})\),每次到一个位置先将 \(f_{(lst_{i - 1, a_i, i}]}\) 加上 \(lst_{i - 1, a_i} - i\) 的贡献,线段树维护就做完了。


浙公网安备 33010602011771号