2023年多校联训NOIP层测试3
T1寄了 \(30\) 分,T2挂了,T3打暴力骗了 \(45\) 分,T4 输出1拿了 \(6\) 分。((我太废物了
\(total = 70 + 0 + 45 + 6 = 121\)
T1 数列变换
题目
思路
两种操作,分别是 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的解释写完了,但电脑突然关机,只好重写这里)
题目
思路
这是一道诈骗题……
考试时因为 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 区间加和
题目
思路
\(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 距离序列
题目
思路
考虑 \(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;
}