模拟四补题报告

李智航

S13494

1.考试得分:

第一题AC,第二题70分,第三题,第四题

2.考试过程:

第一题感觉还行,突然发现A,B,C的值存不下,必须边算边取余,浪费了很多时间;第二题数据范围也很恶心,感觉不优化就爆, 对于查找也很恶心,有时候会把原先的覆盖掉,还需要记忆。第三天卡的更死,打了一个找所有子序列的代码,六重循环,完全崩了(也就是枚举)。第四题直接不会做,看了一眼样例,偏分都没法骗分2,只好搬出了rand()碰运气。

3.考试解析

1.下三个(three)

1.题目:

在这里插入图片描述
6c3e8.png)

2.开始代码和思路

代码如下:

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n;
	cin>>n;
	long long  A=1,B=1,C=1;
	long long CA,CB,CC;
	for(int i=1;i<=n;i++){
		CA=A+A+2*B+C;
		CB=B+A+C;
		CC=C+A+2*B;
		A=CA;
		B=CB;
		C=CC;
		if(A%2!=0){
			A=1;
		}else{
			A=0;;
		}
		if(B%2!=0){
			B=1;
		}else{
			B=0;
		}
		if(C%2!=0){
			C=1;
		}else{
			C=0;
		}
	}
	if(A==1){
		cout<<"odd"<<endl;
	}else{
		cout<<"even"<<endl;
	}
	if(B==1){
		cout<<"odd"<<endl;
	}else{
		cout<<"even"<<endl;
	}
	if(C==1){
		cout<<"odd"<<endl;
	}else{
		cout<<"even"<<endl;
	}
	return 0;
}

思路:就是暴力枚举,因为数据范围太大,所以边枚举边判段是奇数还是偶数。

3.正解

sub1:大模拟
#include<bits/stdc++.h>
using namespace std;
const int M=1e5+10;
int f[M][3], n;
int main(){
	cin >> n;
	f[0][0] = 1;
	f[0][1] = 1;
	f[0][2] = 1;
	for (int i = 0; i < n; i++){
		f[i + 1][0] = f[i][0];
		f[i + 1][1] = f[i][1];
		f[i + 1][2] = f[i][2];
		f[i + 1][0] += f[i][0];
		f[i + 1][0] %= 2;
		f[i + 1][1] += f[i][0];
		f[i + 1][1] %= 2;
		f[i + 1][2] += f[i][0];
		f[i + 1][2] %= 2;
		f[i + 1][0] += f[i][1] * 2 % 2;
		f[i + 1][0] %= 2;
		f[i + 1][2] += f[i][1] * 2 % 2;
		f[i + 1][2] %= 2;
		f[i + 1][0] += f[i][2];
		f[i + 1][0] %= 2;
		f[i + 1][1] += f[i][2];
		f[i + 1][1] %= 2;
	}
	cout << (f[n][0] ? "odd" : "even") << endl;
	cout << (f[n][1] ? "odd" : "even") << endl;
	cout << (f[n][2] ? "odd" : "even") << endl;
	return 0;
}

sub2:思维
#include<iostream>
#include<cstdio>
using namespace std;
int n;
int main(){
	scanf("%d",&n);
	if(n%3==0){
		printf("odd\n");
		printf("odd\n");
		printf("odd\n");
	} 
	else if(n%3==1){
		printf("odd\n");
		printf("odd\n");
		printf("even\n");
	} 
	else if(n%3==2){
		printf("even\n");
		printf("even\n");
		printf("odd\n");
	} 
	return 0;
}

2.合体(fit)

1.题目:

在这里插入图片描述

开始代码:

#include <iostream>
#include <vector>
using namespace std;
int a[1000001],count[1000001];
bool vis[1000001];
int remember[1000001];
#define endl "\n"
int main(){
    int n,m;
    cin>>n>>m;
    for(int i=0;i<n;++i){
        cin>>a[i]; 
        count[a[i]]++;
    }
    int q;
    cin>>q;
    for(int i=0;i<q;++i){
        int x;
        cin>>x; 
        if(x>m){
            cout<<0<<endl;
            continue;
        }else if(x==1){
        	cout<<count[1]<<endl;
        	continue;
		}
		if(vis[x]==false){
        	for(int j=1;j<x;++j){
            	if(count[j]==1||count[j]==0);
            	else{
            		if(count[j]%2==0){
                		count[j+1]+=count[j]/2;
                		count[j]=0;
					}
                	else{
                    	count[j+1]+=count[j]/2;
                    	count[j]=1;
					}
					remember[j+1]=count[j+1];
					vis[x]=true;
				}
			}
		}else{
			cout<<remember[x]<<endl;
			continue;
		}
        cout<<count[x]<<endl;
    }
    return 0;
}

思路:直接暴力。把每个数枚举到x,每次枚举时,每个经过枚举的数都是最大的。
因为j是去x的大小的,所以刚算出的j+1位是最大的,然后remember存储一下,防
止以后的再来查找。那个vis就是判断x会不会出现重复,x重复了话就不进入循环。

正解:

#include<iostream>
#include<cstdio>
using namespace std;
const int N=1e6+25;
int n,m,a[N],q,x;
int t[N],k[N];//t为桶数组,k为合并数组 
int minn=N;
int main(){
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		t[a[i]]++;
		minn=min(a[i],minn);
	} 
	for(int i=minn;i<=m+25;i++){//极端情况出现10^6个相同的数,最多合成log2(10^6)个新数≈20个 
		k[i]=t[i-1]/2;//每个数的合并量=上个数的个数/2
		t[i]+=k[i];//当前数的个数=本身个数+合并量 
	}
	scanf("%d",&q);
	while(q--){
		scanf("%d",&x);
		printf("%d\n",t[x]);
	}
	return 0;
}

3.矩阵(matrix)

1.题目:

在这里插入图片描述

2.开始代码:

#include <iostream>
using namespace std;
long long a[301][301];
long long ans,count;
int main(){
    int n,m;
    cin>>n>>m; 
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            cin >> a[i][j];
        }
    }
    for (int x1 = 0; x1 < n; ++x1) {
        for (int y1 = 0; y1 < m; ++y1) {
            for (int x2 = x1; x2 < n; ++x2) {
                for (int y2 = y1; y2 < m; ++y2) {
					int count=0;
                    for (int i = x1; i <= x2; ++i) {
                        for (int j = y1; j <= y2; ++j) {
                            count=count^a[i][j];
                        }
                    }
                    ans+=count;
                }
            }
        }
    }
	cout<<ans;
    return 0;
}

 

思路:该程序旨在计算二维数组中所有子矩阵的异或总和。首先,输入矩阵的维度和元素。接着,利用四重循环遍历所有可能的子矩阵,通过指定左上角和右下角的坐标,逐个计算子矩阵的异或值。内层的双重循环用于计算当前子矩阵内所有元素的异或。最后,将计算得到的异或值累加到答案中,并输出结果。此方法的时间复杂度较高,适合较小的矩阵。

3.正解:

//子矩阵异或,二维前缀和做法求二维异或和O(n^4)
//二维压一维
/*
枚举起始行i和终止行j
1 2 3 4 5 6
1 2 3 4 5 6
1 2 3 4 5 6
1 2 3 4 5 6
x[k]=a[i][k]~a[j][k] -->一维前缀异或和


0 按位异或一个数的结果是1,代表这个数里1的个数为奇数;结果为0,为偶数
*/
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=305;
int n, m, a[N][N], x[N], xo[N];
int main(){
    cin >> n >> m;
    for(int i=1; i<=n; i++){
        for(int j=1; j<=m; j++){
            cin >> a[i][j];
        }
    }
    ll ans=0;
    for(int i=1; i<=n; i++){//枚举起始行
        memset(x, 0, sizeof x);
        for(int j=i; j<=n; j++){//枚举终止行
            for(int k=1; k<=m; k++){
                x[k]^=a[j][k];
                xo[k]=xo[k-1]^x[k];
            }
            for(int p=0; p<10; p++){
                //按位枚举,拆位,如10101=10000+00100+00001,选带1的,结果最大
                int cnt0=1;//记录第p位为0的个数
                int cnt1=0;//记录第p位为1的个数
                for(int k=1; k<=m; k++){
                    if(xo[k] & (1<<p)){//如果当前是1
                        ans+=1ll*(1<<p)*cnt0;
                        //异或取前面“有意义的”的0的个数,1^0=1
                        cnt1++;
                    }
                    else{
                        ans+=1ll*(1<<p)*cnt1;
                        异或取前面“有意义的”的1的个数,1^0=1
                        cnt0++;
                    }
                }
            }
        }
    }
    cout << ans;
    return 0;
}

思路:该程序通过二维前缀异或和的方式高效计算所有子矩阵的异或和。首先,输入矩阵的维度和元素。然后,枚举起始行和终止行,对每一列进行异或操作,形成一维前缀异或数组。接着,对于每一位二进制位,通过拆位计算当前子矩阵中1和0的个数,利用这些信息更新总异或和。该方法有效降低了时间复杂度,使得在较大矩阵中也能迅速求解。

4.数对(pair)

1.题目:

在这里插入图片描述

开始代码:

#include<bits/stdc++.h>
using namespace std;
int main(){
	cout<<rand()%500;
	return 0;
}

没有一点思路,就是靠运气

3.正解
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e6+5, M=1e6+5;
bool flag=0;//特殊样例全0
int n, m, p, a[M], b[M];
ll num[10], numb[M], nixu[10];//nixu[i]表示b数组+i之后的逆序对个数
ll ans[200], cnt=0;
void jia(ll k){
    ans[0]+=k;
    int pos=0;
    while(1){
        if(ans[pos]>=1000000){//压位高精,一个变量存6位,不再存1位
            ans[pos+1]+=ans[pos]/1000000;
            ans[pos]%=1000000;
            if(++pos>cnt){
                cnt++;
            }
        }
        else break;
    }
}
int main(){
    cin >> n >> m >> p;
    for(int i=1; i<=n; i++) {
        cin >> a[i];
        if(a[i]!=0) flag=1;
    }
    for(int i=1; i<=m; i++){
        cin >> b[i];
        if(b[i]!=0) flag=1;
        numb[b[i]]++;
    }
    //预处理,nixu[i]存储a[i]加到b[1]~b[m]各个数上的逆序对数
    for(int j=0; j<p; j++){//a、b数组范围在0~9,打表预处理,
        memset(num, 0, sizeof num);
        for(int i=1; i<=m; i++){//枚举b
            for(int k=b[i]+1; k<p; k++){//k=b[(i+j)%p]+1;
                //保证数字比b[i]大
                nixu[j] += num[k];
            }
            num[b[i]]++;//num[(i+j)%p]++;
            //记录b[i]次数,为下次循环准备
            b[i]=(b[i]+1)%p;//注释掉
            //保证自增,代替掉模拟a[i]+b[j]的枚举,因为a、b数组元素皆<p
        }
    }
    memset(num, 0, sizeof num);
    //ll ans=0;
    for(int i=1; i<=n; i++){
        //ans+=nixu[a[i]];//块内的逆序对数量,c[(i-1)%m+1]~c[(i-1)*m+m]
        jia(nixu[a[i]]);
        for(int j=0; j<p; j++){
            int x=(j+a[i])%p;//某一个c的值
            for(int k=x+1; k<p; k++){
                //ans+=1ll*numb[j]*num[k];
                //块与块之间的个数(nm太大只能求块与块加、间的),不做优化30
                ll x=1ll*numb[j]*num[k];
                jia(x);
            }
        }
        for(int j=0; j<p; j++){
            num[(j+a[i])%p]+=numb[j];
        }
    }
    if(!flag) cout << 0;
    else{
        cout << ans[cnt];
        for(int i=cnt-1; i>=0; i--){
            printf("%06lld", ans[i]);
        }
    }
    return 0;
}

思路:这个程序的目的是计算两个数组 a 和 b 之间的逆序对数。具体思路如下:读入数据:首先,输入数组 a(长度为 n)和 b(长度为 m),同时检查是否有非零元素,以便后续处理。预处理逆序对:使用一个长度为 p 的数组 nixu 来存储将 a[i] 加到 b 每个元素后产生的逆序对数。通过遍历 b 数组并记录当前元素出现的次数来计算逆序对。计算逆序对总数:遍历数组 a,对于每个 a[i],直接获取在 nixu 中记录的逆序对数。计算块与块之间的逆序对数,通过更新 num 数组来维护各个模 p 值的出现次数,并结合当前的元素 a[i] 来更新结果。高精度输出:由于结果可能很大,使用一个数组 ans 来存储每6位数字,最后进行格式化输出。最终输出逆序对的总数,如果所有元素均为零则输出0。这种方法通过预处理和动态更新,有效地降低了时间复杂度,使得即使在大的输入下也能快速计算结果。

总结

思路还需要加强,时间复杂度的优化也需要加强。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Aaronleoesr

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值