2023年多校联训NOIP层测试3

T1寄了 30 30 30 分,T2挂了,T3打暴力骗了 45 45 45 分,T4 输出1拿了 6 6 6 分。((我太废物了

t o t a l = 70 + 0 + 45 + 6 = 121 total = 70 + 0 + 45 + 6 = 121 total=70+0+45+6=121


T1 数列变换

题目

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6TVH5uWw-1693016824127)(https://img2023.cnblogs.com/blog/3042041/202308/3042041-20230804155534971-1580928048.jpg)]

思路

两种操作,分别是 A:前缀和 B:差分。求操作完后的数组。

我们首先要知道一条性质,前缀和的差分数组就是原数组。

所以只需要记录 A 和 B 的数量。求它们的差值。谁大,暴力计算谁即可。相等时即使原数组。((考试的时候,算 B 没有把个数回正,也就是说有 百分之七十 的数据是 A的数量比B大的。这数据……

剩下的百分之三十的数据,要注意差分时非负,加模数后再加模数。

赛事代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e4+10;
const int mod=998244353;
inline int read(){
	int x=0;bool f=1;char s=getchar();
	while(s<'0'||s>'9'){if(s=='-')f=0;s=getchar();}
	while(s>='0'&&s<='9'){x=(x<<1)+(x<<3)+(s^48);s=getchar();}
	return f?x:-x;
}
inline void write(int x){
	if(x<0) x=-x;
	if(x>9) write(x/10);//递归到最高位,在一层层回溯回来,每次putchar最低位 
	putchar(x%10+'0');
}
int n,k,m,a[N];
map<char,int>q;
signed main(void){
//	scanf("%lld%lld",&n,&m);
	n=read();m=read();
	for(int i=1;i<=n;++i){
//		scanf("%lld",a+i);
		a[i]=read();
	}char s;
	for(int p=1;p<=m;++p){
		scanf(" %c",&s);
		k=read();
		q[s]+=k;
//		if(s=='A'){
//			for(int o=1;o<=k;++o){
//				for(int i=2;i<=n;++i){
//					a[i]=a[i-1]+a[i];
//				}
//			}
//		}
//		if(s=='B'){
//			for(int o=1;o<=k;++o){
//				for(int i=n;i>1;--i){
//					a[i]=a[i]-a[i-1];
//				}
//			}
//			
//		}
	}
	int u=q['A']-q['B'];
	if(u>0){
		for(int o=1;o<=u;++o){
			for(int i=2;i<=n;++i){
				a[i]=(a[i-1]+a[i])%mod;
				if(a[i]<0) a[i]=(a[i]+mod)%mod;
			}
		}
	}
	if(u<0){
		for(int o=1;o<=u;++o){
			for(int i=n;i>1;--i){
				a[i]=(a[i]-a[i-1])%mod;
			}
		}
	}
	for(int i=1;i<=n;++i){
//		printf("%lld ",a[i]%mod);
		write(a[i]%mod);
		putchar(' ');
	}
	return 0;
}

赛后代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e4+10;
const int mod=998244353;
inline int read(){
	int x=0;bool f=1;char s=getchar();
	while(s<'0'||s>'9'){if(s=='-')f=0;s=getchar();}
	while(s>='0'&&s<='9'){x=(x<<1)+(x<<3)+(s^48);s=getchar();}
	return f?x:-x;
}
inline void write(int x){
	if(x<0) x=-x;
	if(x>9) write(x/10);//递归到最高位,在一层层回溯回来,每次putchar最低位 
	putchar(x%10+'0');
}
int n,k,m,a[N];
map<char,int>q;
signed main(void){
//	scanf("%lld%lld",&n,&m);
	n=read();m=read();
	for(int i=1;i<=n;++i){
//		scanf("%lld",a+i);
		a[i]=read();
	}char s;
	for(int p=1;p<=m;++p){
		scanf(" %c",&s);
		k=read();
		q[s]+=k;
//		if(s=='A'){
//			for(int o=1;o<=k;++o){
//				for(int i=2;i<=n;++i){
//					a[i]=a[i-1]+a[i];
//				}
//			}
//		}
//		if(s=='B'){
//			for(int o=1;o<=k;++o){
//				for(int i=n;i>1;--i){
//					a[i]=a[i]-a[i-1];
//				}
//			}
//			
//		}
	}
	int u=q['A']-q['B'];
	if(u>0){
		for(int o=1;o<=u;++o){
			for(int i=2;i<=n;++i){
				a[i]=(a[i-1]+a[i]+mod)%mod;
			}
		}
	}
	if(u<0){
		u=-u;
		for(int o=1;o<=u;++o){
			for(int i=n;i>1;--i){
				a[i]=(a[i]+mod-a[i-1])%mod;
			}
		}
	}
	for(int i=1;i<=n;++i){
//		printf("%lld ",a[i]%mod);
		write(a[i]%mod);
		putchar(' ');
	}
	return 0;
}

T2 超级质数

(本来T2的解释写完了,但电脑突然关机,只好重写这里)

题目

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rFLBKCOA-1693016824128)(https://img2023.cnblogs.com/blog/3042041/202308/3042041-20230804185759563-713504686.jpg)]

思路

这是一道诈骗题……

考试时因为 MLE 炸飞了。考虑内存问题。bool 类型占 一位(B)(sizeof(bool)=1),int 类型占 4 位(sizeof(int)=4)。
这样看,如果所有数组均开为 4 ∗ 1 0 7 4*10^{7} 4107 ,肯定 MLE。

但是存素数的数组只需要开到素数个数即可 4 ∗ 1 0 7 4*10^{7} 4107 2433655 2433655 2433655 个素数,所以只需这么大即可。同理,存超级质数的数组经过计算只需要开到 76888 76888 76888

求超级素数就随便求即可,不过多解释。

code

#include<bits/stdc++.h>
using namespace std;
const int N=4e7+1,M=2433655+1,K=76887+1;//严格注意 
inline int read(){
	int x=0;bool f=1;char s=getchar();
	while(s<'0'||s>'9'){if(s=='-')f=0;s=getchar();}
	while(s>='0'&&s<='9'){x=(x<<1)+(x<<3)+(s^48);s=getchar();}
	return f?x:-x;
}
inline void write(int x){
	if(x<0) x=-x;
	if(x>9) write(x/10);//递归到最高位,在一层层回溯回来,每次putchar最低位 
	putchar(x%10+'0');
}
int n,ans,cnt,tot,a[M],s[K];
bool vis[N],v[10];
inline bool check(int x){
	memset(v,0,sizeof v);
	while(x){
		if(v[x%10]) return 0;
		v[x%10]=1;
		x/=10; 
	}
	return 1;
}
inline void shai(){
	memset(vis,1,sizeof vis);
	vis[0]=vis[1]=0; 
	for(int i=2;i<=N;++i){
		if(vis[i]){
			a[++cnt]=i;
			if(check(i)) s[++tot]=i;
		}
		for(int j=1;j<=cnt&&i*a[j]<=N;++j) {
			vis[i*a[j]]=0;
			if(!(i%a[j])) break; 
		}
	}
	return ;
}
inline int solve(int x){
	return upper_bound(s+1,s+tot+1,x)-s-1; 
}
signed main(void){
	n=read();
	shai();
	for(int l,r,i=1;i<=n;++i){
		l=read();r=read();
		printf("%d\n",solve(r)-solve(l-1));
	}
	
}

T3 区间加和

题目

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jC1VOQ1N-1693016824129)(https://img2023.cnblogs.com/blog/3042041/202308/3042041-20230804185913361-1216924890.jpg)]

思路

45 p t s 45pts 45pts
(赛时)离线处理。当是操作2时,取得最大的 k k k。在暴力执行操作1,2。(注意: i i i 为下标)
code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e6+10;
inline int read(){
	int x=0;bool f=1;char s=getchar();
	while(s<'0'||s>'9'){if(s=='-')f=0;s=getchar();}
	while(s>='0'&&s<='9'){x=(x<<1)+(x<<3)+(s^48);s=getchar();}
	return f?x:-x;
}
inline void write(int x){
	if(x<0) x=-x;
	if(x>9) write(x/10);//递归到最高位,在一层层回溯回来,每次putchar最低位 
	putchar(x%10+'0');
}
int n,a[N];
int ooo[N],k[N],pd[N];
signed main(void){
//	freopen("sum2.in","r",stdin);
//	freopen("1.out","w",stdout);
	n=read();
//	scanf("%lld",&n);
	int maxx=0;
	for(int i=1;i<=n;++i){
		ooo[i]=read();k[i]=read();
//		scanf("%lld%lld",ooo+i,k+i);
		if(ooo[i]==2) maxx=max(maxx,k[i]);
//		pd[i]=ooo[i];
	}
	for(int i=1;i<=n;++i){
		if(ooo[i]==1){
			for(int j=k[i]+1;j<=maxx;++j){
				a[j]+=floor(log2(j-k[i]));
			}
		}
		if(ooo[i]==2){
			write(a[k[i]]);
			putchar('\n');
//			printf("%lld\n",a[k[i]]);
		}
	}
	return 0;
}

100 p t s 100pts 100pts
用线段树或树状数组维护。

T4 距离序列

题目

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FCJDCO9v-1693016824129)(https://img2023.cnblogs.com/blog/3042041/202308/3042041-20230804190654897-87231865.jpg)]

思路

考虑 d p i j dp_{ij} dpij 表示长度为 j j j 时选 i i i 的合法个数。在使用前缀和数组优化。

得到两种情况,向上或者向下。如果 k=0 两种情况都进去了,所以减去一种。

code

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+1,M=5e3+1;
const int mod=998244353;
int n,m,k,dp[M][N],sum[M];
signed main(void){
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=m;++i){
		dp[i][1]=1;
		sum[i]=(sum[i-1]+dp[i][1])%mod;
	}
	for(int j=2;j<=n;++j){
		for(int i=1;i<=m;++i){
			if(i+k<=m) dp[i][j]=(dp[i][j]+(sum[m]-sum[i+k-1]+mod)%mod)%mod;
			if(i-k>=1) dp[i][j]=(dp[i][j]+sum[i-k])%mod;
			if(!k) dp[i][j]=(dp[i][j]-dp[i][j-1]+mod)%mod;
		}
		for(int i=1;i<=m;++i) sum[i]=(sum[i-1]+dp[i][j])%mod;
	}
	int ans=0;
	for(int i=1;i<=m;++i) ans=(ans+dp[i][n])%mod;
	printf("%d\n",ans);
	return 0;
} 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值