NOIp原题比赛总结 2025/6/21
2025/6/21\(\mathbf{} \begin{Bmatrix} \frac{{\Large NOIP-TEST} }{{\color{Yellow}\Large Record} }\mathbf{} {No.1} \end{Bmatrix}\times{}\) NeeDna
t1:「NOIP2021」报数
题意:给出一个数 \(n\) 找出第一个 \(x\) 使得 \(x>s\) 且 \(x\notin P\) ,\(P\) 为字符带有 7 和因数属于 \(P\) 集合的数字。
我最开始计算了每一个点是否在 \(P\) 中,暴力是 \(O(n\sqrt n)\) 的,\(1e7\) 过不去,然后发现重复计算了很多,用埃氏筛的方法可以优化,可以优化成调和级数复杂度,即 \(O(nlogn)\)。有一个优化,若 \(i\) 的因数属于 \(P\) 那么不需要对其进行循环,因为其所有倍数也是其因数的倍数。
code:
#include<bits/stdc++.h>
using namespace std;
const int M=1e7+100;
int T,n,p[M],lin[M],nxt;
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>T;
for(int i=7;i<M;i++){
if(p[i]) continue;//<------这里
int u=i;
while(u){
int q=u%10;
if(q==7){p[i]=1;break;}
u/=10;
}
if(i%7==0){p[i]=1;}
if(p[i]){
for(int j=i;j<M;j+=i){//<------埃及氏筛思想
p[j]=1;
}
}
}
for(int i=1;i<M;i++){
if(!p[i]) lin[nxt]=i,nxt=i;
}
while(T--){
cin>>n;
if(p[n]) cout<<-1<<'\n';
else cout<<lin[n]<<'\n';
}
return 0;
}
t2:「NOIP2020」字符串匹配
hash/kmp+计算前后缀奇数+调和级数复杂度,即 \(O(nlogn)\) 的暴力即可,卡常。
code:
#include<bits/stdc++.h>
#define m(a) memset(a,0,sizeof(a))
using namespace std;
int T,n;
const int N=1e6+1e5;
char s[N];
int ne[N],p[N],l[N],h[35];
int v[35],ans=0;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>T;
while(T--){
m(h),m(ne),m(p),m(l),m(v);ans=0;
cin>>s+1;n=strlen(s+1);
for(int i=2,j=0;i<=n;i++){
while(j&&s[i]!=s[j+1]) j=ne[j];
if(s[i]==s[j+1]) j++;
ne[i]=j;
}
for(int i=n;i>=1;i--){
l[i]=l[i+1];
h[s[i]-'a'+1]++;
if(h[s[i]-'a'+1]%2==1) l[i]++;
else l[i]--;
}
m(h);
for(int i=1;i<=n;i++){
p[i]=p[i-1];
h[s[i]-'a'+1]++;
if(h[s[i]-'a'+1]%2==1) p[i]++;
else p[i]--;
}
for(int i=1;i<n;i++){
if(i>=2){
ans+=v[l[i+1]];
for(int j=i*2;j<n;j+=i){
if(!(i%(j-ne[j]))&&ne[j]){
ans+=v[l[j+1]];
}
else break;
}
}
for(int j=p[i];j<=26;j++) v[j]++;
}
cout<<ans<<'\n';
}
return 0;
}
t3 「NOIP2021」数列
收获:dp的状态定义,见下。

code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define lowbit(x) (x&-x)
const int N = 105,MOD = 998244353;
int n,m,k,val[N],dp[N][35][35][16],ans,inv[N][N],C[N][N];
void init(int n){
for(int i=0;i<=n;i++) C[i][0] = 1;
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
C[i][j] = (C[i-1][j]+C[i-1][j-1])%MOD;
}
}
}
int popcount(int x){
int cnt = 0;
while(x) x -= lowbit(x),cnt++;
return cnt;
}
signed main(){
init(100);
cin>>n>>m>>k;
for(int i=0;i<=m;i++){
cin>>val[i];
inv[i][0] = 1;
for(int j=1;j<=n;j++) inv[i][j] = inv[i][j-1]*val[i]%MOD;
}
dp[0][0][0][0] = 1;
for(int i=0;i<=m;i++){
for(int j=0;j<=n;j++){
for(int l=0;l<=k;l++){
for(int p=0;p<=n/2;p++){
for(int t=0;t<=n-j;t++){
dp[i+1][j+t][l+((t+p)&1)][(t+p)/2] = (dp[i+1][j+t][l+((t+p)&1)][(t+p)/2] + dp[i][j][l][p]*inv[i][t]%MOD*C[n-j][t]%MOD)%MOD;
}
}
}
}
}
for(int i=0;i<=k;i++){
for(int p=0;p<=n/2;p++){
if(i+popcount(p)<=k) ans = (ans + dp[m+1][n][i][p])%MOD;
}
}
cout<<ans;
return 0;
}
t4 「NOIP2020」移球游戏 我不配,改不出来。
大概是这样的:
m(mn+1)
找球 vector/priority_queue
拆一个顶到非空柱子上 需要知道那些柱子没满 顺次处理即可知道
把非当前颜色拿到没满(非拆的柱子顶上)的柱子上 需要知道那些柱子没满 顺次处理即可知道
取出当前球到拆的顶上
放回非当前颜色球 退栈 y--
做完一种颜色后要重新做出一个空柱子 重新算球的位置
平均下来是对的,没有常数
以后要增长代码能力,不要不写代码直接抄!!!
而且要多做下NOIp的题,很有收获。

浙公网安备 33010602011771号