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的题,很有收获。

posted @ 2025-06-21 16:24  NeeDna  阅读(67)  评论(0)    收藏  举报