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}\) 递归出答案,因为这里肯定有一个数不合法。
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\) 的值):

规律:如果第 \(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
题目大意:

浙公网安备 33010602011771号