2026/10/07 模拟赛总结

还没写。

NOIP 模拟赛第 ? 场实际上是我忘了

预估得分:\(100+100+50+60\),实际得分:\(100+100+0+60\),排名 \(3/20\)。

T3 空间又没算好,挂了大量分数(实际上是全 RE 了)。

T3 赛后才发现非常简单。还是怪我没有看出来,虽然我好像即使 T3 \(100\) 分也没有 rk2,前两名一个 AK 一个 \(370\)。

T1

题目大意:

给定一个长度为 \(n\) 的仅由 \(0 \sim 9\) 构成的字符串 \(s\),定义集合 \(S\) 为所有 \(s\) 的子序列构成的数字,请求出集合 \(s\) 的 \(\text{mex}\),即集合中最小未出现的元素。注意数不应含前导 \(0\)。

\(n \le 185\)

本题显然有一个类似数位 dp 的做法。

首先观察性质: 我们从右至左来遍历整个字符串,我们假设已经考虑到了答案的后 \(i\) 位数,那么一定地,我们希望这个 \(i\) 位数的第一位在这个字符串出现的位置尽量靠后,这样前面就有足够的位置留给放第 \(i + 1\) 位数。

于是设计如下状态:\(dp_{i,j,k}\) 表示考虑到答案的后 \(i\) 位数,其中第一位的出现位置在第 \(j\) 位(或之后),且最高位等于 \(k\),是否可行。我们设 \(ex_i\) 表示 \(i\) 在字符串中第一次出现的位置,那么 \(dp_{i,j,k}\) 就可以从 \(dp_{i-1,ex_{k},l}\) 转移。如果转移不了,则直接 \(\text{dfs}\) 递归出答案,因为这里肯定有一个数不合法。

```cpp #include #define int unsigned long long using namespace std; int n,dp[25][1005][15],cnt,ex[1005]; string s; void dfs(int wei,int pos,int dang,int lei) { if(wei == 1) { cout << lei * 10 + dang; exit(0); } for(int i = pos;i <= n;i ++) if(s[i] - '0' == dang) { int ps = 0; for(int j = 0;j < 10;j ++) { if(dp[wei - 1][i + 1][j])continue ; ps = j;break ; } dfs(wei - 1,i + 1,ps,lei * 10 + dang); } dfs(wei - 1,n + 1,0,lei * 10 + dang); } signed main() { freopen("mex.in","r",stdin); freopen("mex.out","w",stdout); cin >> n >> s; s = " " + s; for(int i = 1;i <= n;i ++) { cnt += (s[i] == '0'); if(!ex[s[i] - '0'])ex[s[i] - '0'] = i; } for(int i = 1;i <= n + 1;i ++) for(int j = 0;j <= 9;j ++)dp[0][i][j] = 1; if(!cnt) { cout << "0"; return 0; } for(int i = 1;i <= 20;i ++) { int fl = 0; for(int j = 0;j <= 9;j ++) { if(!ex[j]) { cout << j; return 0; } int pos = 1000; for(int k = 0;k <= 9;k ++) { if(j && !dp[i - 1][ex[j] + 1][k])dfs(i - 1,ex[j] + 1,k,j); else { int pq = 0; for(int l = ex[j] + 1;l <= n + 1;l ++)if(dp[i - 1][l][k])pq = l; pos = min(pos,pq - 1); } } if(pos < ex[j])continue ; while(s[pos] != j + '0')pos --; for(int k = pos;k;k --)dp[i][k][j] = 1; } } } ```

T2

题目大意:

令一个排列是合法排列当且仅当不存在 \(1 \le k < n\) 满足前 \(k\) 个数是 \(1 \sim k\) 的一个排列,且逆序对恰好为 \(n - 1\),给定两个整数 \(n,k\),请找出长度为 \(n\) 的字典序第 \(k\) 小的合法序列。保证有解。

\(n \le 5000,k \le 2^{n - 2}\)

这题也是有暗示了,注意到保证有解,大胆猜测合法数量就 \(2^{n - 2}\) 个。

但是仅知道数量没有用啊,于是考虑打表。

\(n = 7\) 打表结果如下(所有的方案和对应的 \(k−1\) 的值):

image

规律:如果第 \(i\) 位为 \(0\),则填当前未填的第 \(x\) 小,并在后面依次跟上 \(x - 2\) 个数,分别为当前未填的第 \(i\) 小。否则 \(x\) 加一。初始 \(x = 2\)。

嗯所以你找着这个打表结果模拟就好了。

注意到 \(k\) 很大很大,于是你需要一个高精度,用来连续除以 \(2\),复杂度是 \(\mathcal{O}(n^2)\) 的。

#include <iostream>
#include <algorithm>
using namespace std;
int n,a[200005],b[200005],pos[200005];
string k,w;
void dfs(int x,int y)//枚举到第x层,k的第i位
{
    if(x == n)
    {
        for(int i = 1;i <= n;i ++)
            if(!b[i])pos[x] = i;
        return ;
    }
    if(x > n)return ;
    int gs = n + 1;
    for(int i = y;i <= n;i ++)
        if(!a[i]){gs = i;break ;}
    int da = gs - y,l = 1;
    for(int i = x + 1;i <= x + da;i ++)
    {
        while(b[l])l ++;
        pos[i] = l,b[l] = 1;
    }
    while(b[l])l ++;
    l ++;
    while(b[l])l ++;
    pos[x] = l,b[l] = 1;
    dfs(x + da + 1,gs + 1);
}
int main()
{
    freopen("permutation.in","r",stdin);
    freopen("permutation.out","w",stdout);
    cin >> n >> k;
    int mo = k.size() - 1;
    k[mo] --;
    while(k[mo] < '0')k[mo] += 10,k[--mo] --;
    if(k[0] == '0' && k.size() > 1)k.erase(0,1);
    for(int i = 1;i <= n;i ++)
    {
        int p = k.size() - 1;
        a[n - 2 - i + 1] = k[p] % 2;
        if(k == "0")continue ;
        if(k == "1")
        {
            k = "0";
            continue ;
        }
        w = "";
        int du = 0;
        for(int j = 0;j < k.size();j ++)
        {
            int la = k[j] - '0' + du;
            if(w.size() == 0 && la < 2);
            else w += char(la / 2 + '0');
            du = 10 * (la % 2);
        }
        k = w;
        if(k == "")k = "0";
    }
    dfs(1,1);
    for(int i = 1;i <= n;i ++)cout << pos[i] << " ";
}

T3

题目大意:

给定一个长度为 \(n\) 的正整数序列 \(a\),\(q\) 次询问,每次给出两个整数 \(l,r\),请你求出 \(\prod_{i=l}^r a_i\) 的所有质因子的乘积模 \(998244353\) 的值。

\(n,q \le 10^6,a_i \le 10^7\)

唉这波掉大分。

暴力是很简单的,但是线性筛存的数组占用内存太大了导致暴力分都没了。

我来说说这题正解。

就是不难发现一个数很难维护,考虑直接拆成若干个质因数。

于是这样你就得到了一个全新的序列,很关键的是,每次区间询问,你只需要判断不同的数的乘积就行了。

于是你就可以直接按照类似 HH 的项链 的方法,用树状树组和乘法逆元维护即可。

T4

题目大意:

posted @ 2026-10-10 17:32  wuyixiang  阅读(7)  评论(0)    收藏  举报