模拟赛四补题报告

注:本次补题报告没有题目大意,因为作者《突发恶疾》(人话:懒得写),所以不写了(doge)

题目

三个(three)

合体(fit)矩阵(matrix)

数对(pair)

赛中得分1001003020
赛后得分100100100100

题目分析

T1:三个(three)

1.1 问题描述

现在科学家在培养 A,B,C 三种微生物,这三种微生物每一秒都会繁殖出新的微生物,具体规则为:

A 类微生物每一秒会繁殖出 1 个 A 类微生物,1 个 B 类微生物,1 个 C 类微生物。
B 类微生物每一秒会繁殖出 2 个 A 类微生物,2 个 C 类微生物。
C 类微生物每一秒会繁殖出 1 个 A 类微生物,1 个 B 类微生物。

假设所有的微生物都不会死亡,一开始培养皿中有 A,B,C 三种微生物各 1 个,现在问你 n 秒后 A,B,C 三种微生物分别有奇数个还是偶数个。

1.2 输入格式

从文件 three.in 中读取数据。

一行一个整数 n。

1.3 输出格式

输出到文件 three.out 中。

输出总共三行:

第一行:若 n 秒后 A 类微生物有奇数个,输出 odd,否则输出 even
第二行:若 n 秒后 B 类微生物有奇数个,输出 odd,否则输出 even
第三行:若 n 秒后 C 类微生物有奇数个,输出 odd,否则输出 even

1.4 输入样例1
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 数据描述

总共 20 个测试点:

对于测试点 1∼4:1≤n≤3。
对于测试点 5∼8:1≤n≤100。
对于测试点 9∼20:1≤n≤10^6​​。

题目分析

 刚开始随便打个暴力,结果发现会炸(当然包括开long long)

主要是我忘了只判断奇偶就行了,打了个小表找规律,长这个样子:

long long a=1,b=1,c=1;
	for(int i=1;i<=n;i++){
		long long na=a+2*b+c,nb=a+c,nc=a+b*2;
		a=na+a;b=nb+b;c=nc+c;
		cout<<i<<' ';
		if(a%2==0)cout<<"even ";
		else cout<<"odd ";
		if(b%2==0)cout<<"even ";
		else cout<<"odd ";
		if(c%2==0)cout<<"even\n";
		else cout<<"odd\n";
	}

1 odd odd even
2 even even odd
3 odd odd odd
4 odd odd even
5 even even odd
6 odd odd odd
7 odd odd even
8 even even odd
9 odd odd odd
10 odd odd even

…………

发现规律了(doge)

3个为一循环周期,循环为{( odd odd even),(even even odd),(odd odd odd)}

结合一下mod,就能速刷本题

正解代码

#include<bits/stdc++.h>
using namespace std;
int main(){
	//freopen("three.in","r",stdin);
	//freopen("three.out","w",stdout);
	int n;
	scanf("%d",&n);
	if(n%3==0)cout<<"odd\nodd\nodd";
	else if(n%3==1)cout<<"odd\nodd\neven";
	else if(n%3==2)cout<<"even\neven\nodd";
	//fclose(stdin);
	//fclose(stdout);
	return 0;
}

这是出题人家里水管炸了吗?怎么这么水

T2:合体(fit)

2.1 问题描述

现在有 n 个大小范围在 1∼m 中的整数 a1∼an,并且你获得了一项魔法能力。
施加一次魔法能力后,可以将两个相同的数字合并成一个数字,并且这个数字为原来的数字 +1,例如:

有 2,2 这两个数字,施加一次魔法能力后可以将这两个数字合并成一个数字 3。

现在有 q 次询问,每次询问给你一个整数 x,你可以施加任意次数魔法能力,问你这 n 个整数最多能得到多少个整数 x?

2.2 输入格式

从文件 fit.in 中读取数据。

第一行两个整数 n,m。
第二行 nn 个整数 a1∼an​​。
第三行一个整数 q。
接下来 q 行,每行一个整数 x。

2.3 输出格式

输出到文件 fit.out 中。

输出 q 行,对于每个询问,输出对应的答案。

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 数据描述

总共 20 个测试点:

对于测试点 1∼4:1≤n≤10,1≤m≤10,1≤ai≤m,1≤q≤10,1≤x≤m。
对于测试点 5∼6:1≤n≤10^6,m=1,ai=1,q=1,x=1。
对于测试点 7∼8:1≤n≤10^6,m=5,1≤ai≤m,1≤x≤m。
对于测试点 9∼20:1≤n≤10^6,1≤m≤10^6,1≤ai≤m,1≤x≤m。

题目分析

第一反应:2048,暴力打表!

第二反应:多看一眼就会爆炸 

第三反应:那就不在循环里打表了

然后就A了(doge)

很简单吧。

首先开一个桶,统计个数(谁叫他数据这么小)

接着在循环外面统计一下合成之后的个数

原版的合成题都需要两步的,第一步将合成结果加,第二步将原料减

但是这个因为是要求最大,并不要求一定要不超,可以省去第二步

接着直接输出就完事了

正解代码

#include<bits/stdc++.h>
using namespace std;
int n,m,q;
int a[1000100];
int main(){
	//freopen("fit.in","r",stdin);
	//freopen("fit.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=0;i<n;i++){
		int x;
		scanf("%d",&x);
		a[x]++;
	}for(int i=2;i<=m;i++){
		a[i]+=a[i-1]/2;
	}scanf("%d",&q);
	for(int i=1;i<=q;i++){
		int x;
		scanf("%d",&x);
		printf("%d\n",a[x]);
	}
	//fclose(stdin);
	//fclose(stdout);
	return 0;
}
/*
10 4
1 1 1 2 1 3 4 1 2 3
5
1
2
3
4
4
*/

快走快走,给出题人家里修水管去!!!

T3:矩阵(matrix) 

3.1 问题描述

现在给你一个 n 行 m 列的矩阵,矩阵上每个格子有一个整数,其中第 i 行第 j 列对应的格子上的整数为 gi,j。
现在定义该矩阵的一个子矩阵的快乐值为该子矩阵上的所有数字的异或和。

一组数字 a1,a2,...,an 的异或和为 a1 xor a2 xor ... xor an。(其中 xor 表示按位异或运算)

现在问你,该矩阵的所有子矩阵的快乐值之和为多少?

3.2 输入格式

从文件 matrix.in 中读取数据。

第一行两个整数 n,m。
接下来 n 行,每行 m 个整数,表示该矩阵。

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∼4:1≤n,m≤10,0≤gi,j<2^10。
对于测试点 5∼6:1≤n,m≤300,gi,j=1。
对于测试点 7∼12:1≤n,m≤300,0≤gi,j≤1。

对于测试点 13∼20:1≤n,m≤300,0≤gi,j<2^10。

赛中思路

看到这题,本来往出题人家里跑的我瞬间瞬移回来了,这哪里是不水啊,这(##中国粗口##)是岩浆吧。

 本来打算骗60的,思路倒是有,但想不出来怎样才能使7~12不爆炸,所以果断放弃(doge)

随便骗了个小30

赛中代码

#include<bits/stdc++.h>
using namespace std;
int a[305][305],xo[305][305];
int ans=0;
// x1y1x2y2=xo[x2][y2]^xo[x2][y1-1]^xo[x1-1][y2]^xo[x1-1][y1-1]
int main(){
	//freopen("matrix.in","r",stdin);
	//freopen("matrix.out","w",stdout);
	int n,m,tmp=0;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			scanf("%d",&a[i][j]);
			if(a[i][j]!=1)tmp=1;
			xo[i][j]=a[i][j] xor xo[i-1][j] xor xo[i][j-1] xor xo[i-1][j-1];
		}
	}if(!tmp){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				int x=n-i+1,y=m-j+1;
				if((i*j)%2==1)ans+=x*y;
			}
		}cout<<ans;
	}else{
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				for(int x=1;x<=i;x++){
					for(int y=1;y<=j;y++){
						ans+=xo[i][j] xor xo[i][y-1] xor xo[x-1][j] xor xo[x-1][y-1];
					}
				}
			}
		}cout<<ans;
	}
	//fclose(stdin);
	//fclose(stdout);
	return 0;
}
/*
5 4
3 2 1 2
3 3 5 5
8 7 3 6
1 1 1 1
2 3 9 9
*/

正解思路

稍等一下,让我平复一下心情,因为这玩意儿实在是太(##中国粗口##)难 

首先呢,我们要清楚正儿八经的30分代码

1.这是个异或没问题吧?

好了结束了

因为他是个异或所以他结束了

(世界上跟我上面的有点像)

就是异或的鬼才公式a^b^b=a

啊哈,这可真是太有乐子了!(突然阿哈化)

那么就存在一个叫做前缀和(或者说是前缀异或)的奇葩东西,相较于原版前缀和,它做了一个微小但重要的一步(人话:把+改成了^),当然,也有二维前缀和(前缀异或)

有了这两个鬼才,我们就可以成功骗30分!

(看我代码去,我没写其他代码(doge))

接着是另一个同样30分,但是比刚才那个有用的代码——一维前缀异或!

那么怎么转异或呢?小科技(人话:不知道)

大概意思是确定起始行和结束行,然后通过这个与异或和来算,简单说一下,不多说了。

for(int sx=1;sx<=n;sx++){
		memset(x,0,sizeof(x));
		for(int ex=sx;ex<=n;ex++){
			for(int j=1;j<=m;j++){
				x[j]=x[j]^a[ex][j];
				xo[j]=xo[j-1]^x[j];
			}
}

(别看了,没有其他代码)

但是有一个问题,就是一维异或和二维一样,仍然需要四层来计算答案,所以码…… 

OK啊,正解来了。在刚才里面,想要优化只能优化答案的算法,那么因为给出小于1024,所以我们可以直接转二进制,然后通过二进制的按位相加后按权展开就完事了

那么,代码,启动!!!!!!!!!!

正解代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int a[305][305],xo[305],x[305];
ll ans=0;
// x1y1x2y2=xo[x2][y2]^xo[x2][y1-1]^xo[x1-1][y2]^xo[x1-1][y1-1]
int main(){
	//freopen("matrix.in","r",stdin);
	//freopen("matrix.out","w",stdout);
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			scanf("%d",&a[i][j]);
		}
	}for(int sx=1;sx<=n;sx++){
		memset(x,0,sizeof(x));
		for(int ex=sx;ex<=n;ex++){
			for(int j=1;j<=m;j++){
				x[j]=x[j]^a[ex][j];
				xo[j]=xo[j-1]^x[j];
			}for(int i=0;i<10;i++){
				int cnt0=1,cnt1=0;
				for(int k=1;k<=m;k++){
					if(xo[k] & (1<<i)){
						ans+=1ll*(1<<i)*cnt0;
						cnt1++;
					}else{
						ans+=1ll*(1<<i)*cnt1;
						cnt0++;
					}
				}
			}
		}
	}cout<<ans;
	//fclose(stdin);
	//fclose(stdout);
	return 0;
}
/*
5 4
3 2 1 2
3 3 5 5
8 7 3 6
1 1 1 1
2 3 9 9
*/

T4:数对(pair)

4.1 问题描述

给你一个长度为 n 的数列 a1,a2,...,an​​。
再给你一个长度为 mm 的数列 b1,b2,...,bm​​。
现在再再再给你一个正整数 p,让你生成一个长度为 n×m 的数列 c1,c2,...,cn×m。
其中满足 c(i−1)×m+j=(ai+bj) mod p。
现在问你数列 cc 中有多少个数对 (i,j) 满足 i<j 且 ci>cj?

4.2 输入格式

从文件 pair.in 中读取数据。

第一行两个整数 n,m,p。
第二行 n 个整数,表示 a1∼an​​。
第三行 m 个整数,表示 b1∼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 数据描述

总共 20 个测试点:

对于测试点 1∼4:1≤n,m≤100,1≤p≤10,0≤ai,bi<p。
对于测试点 5∼6:1≤n,m≤1000,1≤p≤10,0≤ai,bi<p。
对于测试点 7∼8:1≤n,m≤1000000,p=2,0≤ai,bi<p。
对于测试点 9∼20:1≤n,m≤1000000,1≤p≤10,0≤ai,bi<p。

看着题目描述,我仿佛见到了我太奶 

终于明白为什么花儿那么红,前两道题那么简单了

赛中思路

暴力骗20

赛中代码

不会吧,这也要代码?!

#include<bits/stdc++.h>
using namespace std;
int a[1000005],b[1000005],c[1000005];
int main(){
	//freopen("pair.in","r",stdin);
	//freopen("pair.out","w",stdout);
	int n,m,p;
	scanf("%d%d%d",&n,&m,&p);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int i=1;i<=m;i++)scanf("%d",&b[i]);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			c[(i-1)*m+j]=(a[i]+b[j])%p;
		}
	}int ans=0;
	for(int i=1;i<n*m;i++){
		for(int j=i+1;j<=n*m;j++){
			if(c[i]>c[j])ans++;
		}
	}cout<<ans;
	//fclose(stdin);
	//fclose(stdout);
	return 0;
}
/*
6 7 10
1 1 4 5 1 4
1 9 1 9 8 1 0
*/

正解思路

首先,因为数太大了所以要有个高精

其次,因为数普通高精时间太长了所以用压位高精

再其次,我不会压位(doge)

先把压位扔上来吧

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;
    }
}

然后就是分治一下,具体看注释。

正解代码

#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;
}

AK快乐!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值