前言
oh fuck wa 这张卷子前两题非常简单,bbbbbbbbbbbbut后两题两个大毒瘤
第一题三个(three)
1.1 问题描述
现在科学家在培养 A,B,CA,B,C 三种微生物,这三种微生物每一秒都会繁殖出新的微生物,具体规则为:
AA 类微生物每一秒会繁殖出 11 个 AA 类微生物,11 个 BB 类微生物,11 个 CC 类微生物。
BB 类微生物每一秒会繁殖出 22 个 AA 类微生物,22 个 CC 类微生物。
CC 类微生物每一秒会繁殖出 11 个 AA 类微生物,11 个 BB 类微生物。
假设所有的微生物都不会死亡,一开始培养皿中有 A,B,CA,B,C 三种微生物各 11 个,现在问你 nn 秒后 A,B,CA,B,C 三种微生物分别有奇数个还是偶数个。
1.2 输入格式
从文件 three.in
中读取数据。
一行一个整数 nn。
1.3 输出格式
输出到文件 three.out
中。
输出总共三行:
第一行:若 nn 秒后 AA 类微生物有奇数个,输出 odd
,否则输出 even
。
第二行:若 nn 秒后 BB 类微生物有奇数个,输出 odd
,否则输出 even
。
第三行:若 nn 秒后 CC 类微生物有奇数个,输出 odd
,否则输出 even
。
1.4 输入样例
3
1.5 输出样例1
odd
odd
odd
1.6 输入样例2
4
1.7 输出样例2
odd
odd
even
1.8 输入样例3
233
1.9 输出样例3
even
even
odd
1.10 数据描述
总共 2020 个测试点:
对于测试点 1∼41∼4:1≤n≤31≤n≤3。
对于测试点 5∼85∼8:1≤n≤1001≤n≤100。
对于测试点 9∼209∼20:1≤n≤1061≤n≤106。
非常简单,就不多说了
#include<bits/stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<utility>
#include<cstdlib>
#define ll long long
#define pii pair<long long,long long>
#define endl "\n"
using namespace std;
ll a=1,b=1,c=1,n,oa=1,ob=1,oc=1;
int main(){
//freopen("three.in","r",stdin);
//freopen("three.out","w",stdout);
cin>>n;
for(int i=1;i<=n;i++){
a=2*oa+ob*2+oc;
b=oa+oc+ob;
c=oa+2*ob+oc;
a%=10,b%=10,c%=10;
//cout<<i<<" "<<a<<" "<<b<<" "<<c<<endl;
oa=a,ob=b,oc=c;
}
if(a%2==0){
cout<<"even"<<endl;
}
else{
cout<<"odd"<<endl;
}
if(b%2==0){
cout<<"even"<<endl;
}
else{
cout<<"odd"<<endl;
}
if(c%2==0){
cout<<"even"<<endl;
}
else{
cout<<"odd"<<endl;
}
return 0;
}
第二题合体(fit)
2.1 问题描述
现在有 nn 个大小范围在 1∼m1∼m 中的整数 a1∼ana1∼an,并且你获得了一项魔法能力。
施加一次魔法能力后,可以将两个相同的数字合并成一个数字,并且这个数字为原来的数字 +1+1,例如:
有 2,22,2 这两个数字,施加一次魔法能力后可以将这两个数字合并成一个数字 33。
现在有 qq 次询问,每次询问给你一个整数 xx,你可以施加任意次数魔法能力,问你这 nn 个整数最多能得到多少个整数 xx?
2.2 输入格式
从文件 fit.in
中读取数据。
第一行两个整数 n,mn,m。
第二行 nn 个整数 a1∼ana1∼an。
第三行一个整数 qq。
接下来 qq 行,每行一个整数 xx。
2.3 输出格式
输出到文件 fit.out
中。
输出 qq 行,对于每个询问,输出对应的答案。
2.4 输入样例
10 4
1 1 1 2 1 3 4 1 2 3
5
1
2
3
4
4
2.5 输出样例
5
4
4
3
3
2.6 数据描述
总共 2020 个测试点:
对于测试点 1∼41∼4:1≤n≤10,1≤m≤10,1≤ai≤m,1≤q≤10,1≤x≤m1≤n≤10,1≤m≤10,1≤ai≤m,1≤q≤10,1≤x≤m。
对于测试点 5∼65∼6:1≤n≤106,m=1,ai=1,q=1,x=11≤n≤106,m=1,ai=1,q=1,x=1。
对于测试点 7∼87∼8:1≤n≤106,m=5,1≤ai≤m,1≤x≤m1≤n≤106,m=5,1≤ai≤m,1≤x≤m。
对于测试点 9∼209∼20:1≤n≤106,1≤m≤106,1≤ai≤m,1≤x≤m1≤n≤106,1≤m≤106,1≤ai≤m,1≤x≤m。
解释
这题也十分简单
做法1:
对于每次查询x,都暴力两两合并比x小的所有数字。
做法2:
对于做法1的合并过程进行优化。
做法3:
对于做法2的查询过程进行优化。
考虑到数字大小不大。我们可以记录一个答案数组ans[i]。
ans[i]表示为查询 i 的答案。也就是ans[i]表示最多可以产生多少个i。直接统计每个数字的数量cnt[i],进行递推。
,其中 cnt表示数字 出现的次数。
然后直接递推即可。
对于查询,O(1)输出即可。
代码
#include<bits/stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<utility>
#include<cstdlib>
#define ll long long
#define pii pair<long long,long long>
#define endl "\n"
using namespace std;
ll n,m,a[1000006],x,q;
int main(){
//freopen("fit.in","r",stdin);
//freopen("fit.out","w",stdout);
cin>>n>>m;
for(int i=1;i<=n;i++){
scanf("%d",&x);
a[x]++;
}
for(int i=2;i<=m;i++){
a[i]+=a[i-1]/2;
}
cin>>q;
while(q--){
scanf("%d",&x);
printf("%d\n",a[x]);
}
return 0;
}
第三题矩阵(matrix)
3.1 问题描述
现在给你一个 nn 行 mm 列的矩阵,矩阵上每个格子有一个整数,其中第 ii 行第 jj 列对应的格子上的整数为 gi,jgi,j。
现在定义该矩阵的一个子矩阵的快乐值为该子矩阵上的所有数字的异或和。
一组数字 a1,a2,...,ana1,a2,...,an 的异或和为 a1 xor a2 xor ... xor ana1 xor a2 xor ... xor an。(其中 xorxor 表示按位异或运算)
现在问你,该矩阵的所有子矩阵的快乐值之和为多少?
3.2 输入格式
从文件 matrix.in
中读取数据。
第一行两个整数 n,mn,m。
接下来 nn 行,每行 mm 个整数,表示该矩阵。
3.3 输出格式
输出到文件 matrix.out
中。
一行一个整数,表示答案。
3.4 输入样例
5 4
3 2 1 2
3 3 5 5
8 7 3 6
1 1 1 1
2 3 9 9
3.5 输出样例
822
3.6 数据描述
总共 20 个测试点:
对于测试点 1∼41∼4:1≤n,m≤10,0≤gi,j<2101≤n,m≤10,0≤gi,j<210。
对于测试点 5∼65∼6:1≤n,m≤300,gi,j=11≤n,m≤300,gi,j=1。
对于测试点 7∼127∼12:1≤n,m≤300,0≤gi,j≤11≤n,m≤300,0≤gi,j≤1。
对于测试点 13∼2013∼20:1≤n,m≤300,0≤gi,j<2101≤n,m≤300,0≤gi,j<210。
解释
大毒瘤来了
解法1:二维前缀和
维护二维异或前缀和数组,之后枚举子矩阵的左上角和右下角,计算区间异或值即可,时间复杂度为
代码自行实现。期望得分20分。
解法2:
考虑将二维数组压成一维的;
枚举起始行 i 和终止行 j ,这个范围内的每一列都求异或值 x[k] ;即 x[k] 为 a[i][k] ~ a[j][k] 的异或值。
之后再对于x数组,求前缀异或值,然后枚举其左右端点,计算区间异或值即可。时间复杂度也是 ,期望得分20分 。
考虑优化:
对于上述代码,枚举完起始行和终止行之后,还需要 去计算结果,这一部分可以考虑优化。
可以按位去考虑,对于某个区间,按位异或之后的值,的某一位,是否为1。只需要考虑这个区间内的这一位的1的个数是奇数个还是偶数个。
也可以考虑xo数组 ans += (xo[r] ^ xo[l - 1]); ,按位考虑,某一位如果想被累计到答案里,那么xo[r]的这一位和xo[l-1]的这一位,不相同。所以可以考虑把xo拆位,如果当前这一位是1,那么就考虑它前面这一位是0的情况有多少个,反之同理。
代码
#include<bits/stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<utility>
#include<cstdlib>
#define ll long long
#define pii pair<long long,long long>
#define endl "\n"
using namespace std;
int n,m,a[305][305],b[305],sum[305];
ll ans;
int main(){
//freopen("matrix.in","r",stdin);
//freopen("matrix.out","w",stdout);
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
}
}
for(int u=1;u<=n;u++){
memset(b,0,sizeof(b));
for(int d=u;d<=n;d++){
for(int j=1;j<=m;j++){
b[j]^=a[d][j];
sum[j]=sum[j-1]^b[j];
}
for(int p=0;p<10;p++){
ll cnt[2]={1,0};
for(int j=1;j<=m;j++){
int t=(sum[j]>>p)&1;
ans+=(1<<p)*cnt[t^1];
cnt[t]++;
}
}
}
}
cout<<ans<<endl;
return 0;
}
第四题数对(pair)
4.1 问题描述
给你一个长度为 nn 的数列 a1,a2,...,ana1,a2,...,an。
再给你一个长度为 mm 的数列 b1,b2,...,bmb1,b2,...,bm。
现在再再再给你一个正整数 pp,让你生成一个长度为 n×mn×m 的数列 c1,c2,...,cn×mc1,c2,...,cn×m。
其中满足 c(i−1)×m+j=(ai+bj) mod pc(i−1)×m+j=(ai+bj) mod p。
现在问你数列 cc 中有多少个数对 (i,j)(i,j) 满足 i<ji<j 且 ci>cjci>cj?
4.2 输入格式
从文件 pair.in
中读取数据。
第一行两个整数 n,m,pn,m,p。
第二行 nn 个整数,表示 a1∼ana1∼an。
第三行 mm 个整数,表示 b1∼bmb1∼bm。
4.3 输出格式
输出到文件 pair.out
中。
一行一个整数,表示答案。
4.4 输入样例
6 7 10
1 1 4 5 1 4
1 9 1 9 8 1 0
4.5 输出样例
294
4.6 数据描述
总共 2020 个测试点:
对于测试点 1∼41∼4:1≤n,m≤100,1≤p≤10,0≤ai,bi<p1≤n,m≤100,1≤p≤10,0≤ai,bi<p。
对于测试点 5∼65∼6:1≤n,m≤1000,1≤p≤10,0≤ai,bi<p1≤n,m≤1000,1≤p≤10,0≤ai,bi<p。
对于测试点 7∼87∼8:1≤n,m≤1000000,p=2,0≤ai,bi<p1≤n,m≤1000000,p=2,0≤ai,bi<p。
对于测试点 9∼209∼20:1≤n,m≤1000000,1≤p≤10,0≤ai,bi<p1≤n,m≤1000000,1≤p≤10,0≤ai,bi<p。
解释
我们观察到 数组是可以分成 块,每一块有 个数字。 然后我们求逆序对可以块内求,然后再块与块之间求。
对于块内观察到值域非常小,我们可以对 b 数组记录数字出现次数num。
枚举到当前数字x,计算逆序数时,可以枚举比x大的数字的出现次数即可。也就是记录 num[b[i]+1]~num[p-1] 的和。
考虑b后续需要变化(+a[j]) 。所有我们记录一个数组nixu[k]。表示为对于b数组所有数组都加k之后,逆序数为多少。
对于块与块我们可以记录之前所有数字的出现次数,当前块一定是在之前块的后面,所以直接枚举值域,统计逆序数即可。
代码
#include<bits/stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<utility>
#include<cstdlib>
#define ll long long
#define pii pair<long long,long long>
#define endl "\n"
using namespace std;
int n,m,p,a[1000006],b[1000006];
ll cntb[11],res[11],vis[11];
void write(__int128 x){
if(x>9){
write(x/10);
}
putchar(char(x%10+'0'));
}
int main(){
//freopen("pair.in","r",stdin);
//freopen("pair.out","w",stdout);
cin>>n>>m>>p;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=m;i++){
cin>>b[i];
cntb[b[i]]++;
}
for(int k=0;k<p;k++){
memset(vis,0,sizeof(vis));
for(int i=1;i<=m;i++){
for(int j=b[i]+1;j<p;j++){
res[k]+=vis[j];
}
vis[b[i]]++;
b[i]=(b[i]+1)%p;
}
}
__int128 ans=0;
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++){
ans+=res[a[i]];
for(int k=0;k<p;k++){
for(int j=(a[i]+k)%p+1;j<p;j++){
ans+=cntb[k]*vis[j];
}
}
for(int k=0;k<p;k++){
vis[(a[i]+k)%p]+=cntb[k];
}
}
write(ans);
return 0;
}
see you again