2026.9.14 考试总结

考试总结

估:100 + 100 + 100 + 100

实:100 + 100 + 100 + 100 rk1

水水 dp 场。

B Coding

弱化版:P4124 [CQOI2016] 手机号码

改成了 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

f6bf7b28f6a0e7897cb66dce645080ad

滑动窗口。对于每个 \(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

原题:Partition Game

我们设 \(dp_{i, j}\) 表示前 \(i\) 个分成 \(j\) 段的最小代价,我们考虑第一维枚举 \(j\) 然后枚举 \(i\)。我们考虑设 \(lst_{i, c}\) 表示 \(i\) 之前最后一个元素 \(c\) 出现的位置,\(nxt_i\) 表示下一次,那么转移可以写成:

\[dp_{i, j} = \sum_c lst_{i, c} + \min_{k < i} \{ -\sum_c nxt_{k, c} + dp_{k, j - 1} \} \]

\(\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\) 的贡献,线段树维护就做完了。

code

posted @ 2026-09-14 21:37  ACehomoxue  阅读(8)  评论(0)    收藏  举报