2023年多校联训NOIP层测试3

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

\(total = 70 + 0 + 45 + 6 = 121\)


T1 数列变换

题目

image

思路

两种操作,分别是 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的解释写完了,但电脑突然关机,只好重写这里)

题目

image

思路

这是一道诈骗题……

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

但是存素数的数组只需要开到素数个数即可 \(4*10^{7}\)\(2433655\) 个素数,所以只需这么大即可。同理,存超级质数的数组经过计算只需要开到 \(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 区间加和

题目

image

思路

\(45pts\)
(赛时)离线处理。当是操作2时,取得最大的 \(k\)。在暴力执行操作1,2。(注意:\(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;
}

\(100pts\)
用线段树或树状数组维护。

T4 距离序列

题目

image

思路

考虑 \(dp_{ij}\) 表示长度为 \(j\) 时选 \(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;
} 
posted @ 2023-08-04 15:59  GOD_HJ  阅读(47)  评论(0编辑  收藏  举报