李奕泽模拟4补题报告

目录

一、分数报告:

T1

T2

T3

T4

二、赛中概括:

三、题目分析:

三个(three)

1.1 问题描述

1.2 输入格式

1.3 输出格式

1.4 输入样例1

1.5 输出样例1

1.6 输入样例2

1.7 输出样例2

1.8 输入样例3

1.9 输出样例3

1.10 数据描述

合体(fit)

2.1 问题描述

2.2 输入格式

2.3 输出格式

2.4 输入样例

2.5 输出样例

2.6 数据描述

3.1 问题描述

3.2 输入格式

3.3 输出格式

3.4 输入样例

3.5 输出样例

3.6 数据描述


一、分数报告:

题目分数
T1
100
T2
70
T3
0
T4
0

二、赛中概括:

第一题是一道模拟,第二题想对了思路,但因为TLE(没用scanf和printf),所以当数据 > 1e6时,就要用scanf和printf;后两道题难度飙升(不是给人做的):第三题直接打了个暴力,结果1分也没得;第四题因为不会,输出1,赛后才知道输出0可以拿10分。

三、题目分析:

三个(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 输入格式

一行一个整数 n。

1.3 输出格式

输出总共三行:

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

1.4 输入样例1
3
1.5 输出样例1
  1. odd
  2. odd
  3. odd
1.6 输入样例2
4
1.7 输出样例2
  1. odd
  2. odd
  3. even
1.8 输入样例3
233
1.9 输出样例3
  1. even
  2. even
  3. odd
1.10 数据描述

总共 2020 个测试点:

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

#include<iostream>
#include<cstdio>
using namespace std;
long long a[1000010],b[1000010],c[1000010];
int main(){
	int n;
	cin >> n;
	a[0] = b[0] = c[0] = 1;
	for(int i = 1;i <= 1000000;i++){
		a[i] = a[i-1] + a[i-1];//根据题意模拟
		b[i] = b[i-1] + a[i-1];
		c[i] = c[i-1] + a[i-1];
		a[i] += b[i-1] * 2;
		c[i] += b[i-1] * 2;
		a[i] += c[i-1];
		b[i] += c[i-1];
		a[i] %= 2;//数据太大,要取摸
		b[i] %= 2;
		c[i] %= 2;
	}
	if(a[n] % 2 == 0) cout << "even\n";
	else cout << "odd\n";
	if(b[n] % 2 == 0) cout << "even\n";
	else cout << "odd\n";
	if(c[n] % 2 == 0) cout << "even\n";
	else cout << "odd\n";
	fclose(stdin);
	fclose(stdout);
	return 0;
}

合体(fit)

2.1 问题描述

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

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

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

2.2 输入格式

第一行两个整数 n,m。
第二行 n 个整数 a​1​​∼a​n​​。
第三行一个整数 qq。
接下来 q 行,每行一个整数 x。

2.3 输出格式

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

2.4 输入样例
  1. 10 4
  2. 1 1 1 2 1 3 4 1 2 3
  3. 5
  4. 1
  5. 2
  6. 3
  7. 4
  8. 4
2.5 输出样例
  1. 5
  2. 4
  3. 4
  4. 3
  5. 3
2.6 数据描述

总共 2020 个测试点:

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

 

 桶排序合并,求最优,把之前能合并的都合并:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int t[1000010];
int arr[1000010];
int a[1000010];
bool cmp(int a,int b){
	return a < b;
}
int main(){
	int n,m;
	cin >> n >> m;
	for(int i = 1;i <= n;i++){
		cin >> arr[i];
		t[arr[i]] ++;
	}
	int q;
	cin >> q;
	for(int i = 1;i <= q;i++){
		cin >> a[i];
	}
	sort(a+1,a+q+1,cmp);
	int cnt = 1;
	for(int i = 1;i <= q;i++){//
		while(cnt < a[i]){// 
			t[cnt+1] += t[cnt] / 2;
			t[cnt] %= 2;
			cnt ++;
		}
		cout << t[cnt] << "\n";
	}
	fclose(stdin);
	fclose(stdout);
	return 0;
}

 优化:把while移到外面,变成单层循环(不要忘了加scanf,printf)

 

#include<iostream>
#include<cstdio>
using namespace std;
int t[1000010];
int arr[1000010];
int a[1000010];
bool cmp(int a,int b){
	return a < b;
}
int main(){
	int n,m;
	cin >> n >> m;
	for(int i = 1;i <= n;i++){
		scanf("%d",&arr[i]);
		t[arr[i]] ++;
	}
	int q;
	scanf("%d",&q);
	int cnt = 1;
	for(int i = 1;i <= m;i++){
		t[cnt+1] += t[cnt] / 2;
		cnt ++;
	}
	int x;
	for(int i = 1;i <= q;i++){
		scanf("%d",&x);
		printf("%d\n",t[x]);
	}
	fclose(stdin);
	fclose(stdout);
	return 0;
}

 

矩阵(matrix)

时间限制:2秒        内存限制:256M

3.1 问题描述

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

一组数字 a​1​​,a​2​​,...,a​n​​ 的异或和为 a​1​​ xor a​2​​ xor ... xor a​n​​。(其中 xor 表示按位异或运算)

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

3.2 输入格式

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

3.3 输出格式

一行一个整数,表示答案。

3.4 输入样例
  1. 5 4
  2. 3 2 1 2
  3. 3 3 5 5
  4. 8 7 3 6
  5. 1 1 1 1
  6. 2 3 9 9
3.5 输出样例
822
3.6 数据描述

总共 20 个测试点:
对于测试点 1∼4:1≤n,m≤10,0≤g​i,j​​<2​^10​​。
对于测试点 5∼6:1≤n,m≤300,g​i,j​​=1。
对于测试点 7∼12:1≤n,m≤300,0≤g​i,j​​≤1。
对于测试点 13∼20:1≤n,m≤300,0≤g​i,j​​<2​^10​​。

可以用前缀和的思路,在输入时将前缀和解决,枚举正方形左上方的点和右下方的点(三十分代码):

#include<iostream>
#include<cstdio>
using namespace std;
int arr[310][310],c[310][310]
int main(){
	
	int n,m;
	cin >> n >> m;
	bool bl = 1;
	for(int i = 1;i <= n;i++){
		for(int j = 1;j <= m;j++){
			cin >> arr[i][j];
			if(c[i][j-1] + c[i-1][j] - c[i-1][j-1] == 0){
				c[i][j] = arr[i][j];
			}else{
				c[i][j] = arr[i][j] ^ (c[i][j-1] ^ c[i-1][j] ^c[i-1][j-1]);
			}
		}
	}

	int sum = 0;
	for(int i = 1;i <= n;i++){
		for(int j = 1;j <= m;j++){
			for(int a = i;a <= n;a++){
				for(int b = j;b <= m;b++){
					sum += c[a][b] ^ c[i][j] ^ c[a][j] ^ c[i][b];//因为a^b^b = a;
				}
			}
		}
	}
	cout << sum;
	
	fclose(stdin);
	fclose(stdout);
	return 0;
}

 优化:

 运用前缀和+位运算+思维(你就说你能不能看懂吧)↓↓↓

AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int arr[310][310],x[310],xo[310];
int main(){
	int n,m;
	cin >> n >> m;
	for(int i = 1;i <= n;i++){
		for(int j = 1;j <= m;j++){
			cin >> arr[i][j];
		}
	}
	int ans = 0;
	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] ^ arr[ex][j];
				xo[j] = xo[j-1] ^ x[j];
			}
			for(int i = 0;i <= 9;i++){
				int cnt0 = 1;
				int cnt1 = 1;
				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;
}
/*
..0

*/

数对(pair)

时间限制:2秒        内存限制:256M

4.1 问题描述

给你一个长度为 n 的数列 a​1​​,a​2​​,...,a​n​​。
再给你一个长度为 m 的数列 b​1​​,b​2​​,...,b​m​​。
现在再再再给你一个正整数 p,让你生成一个长度为 n×m 的数列c​1​​,c​2​​,...,c​n×m​​。
其中满足 c​(i−1)×m+j​​=(a​i​​+b​j​​) mod p。
现在问你数列 cc 中有多少个数对(i,j) 满足 i<j 且 c​i​​>c​j(逆序对)​​?

4.2 输入格式

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

第一行两个整数 n,m,pn,m,p。
第二行 nn 个整数,表示 a1∼ana​1​​∼a​n​​。
第三行 mm 个整数,表示 b1∼bmb​1​​∼b​m​​。

4.3 输出格式

输出到文件 pair.out 中。

一行一个整数,表示答案。

4.4 输入样例
 
 
  1. 6 7 10
  2. 1 1 4 5 1 4
  3. 1 9 1 9 8 1 0
4.5 输出样例
 
 
  1. 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≤a​i​​,b​i​​<p。
对于测试点 5∼65∼6:1≤n,m≤1000,1≤p≤10,0≤ai,bi<p1≤n,m≤1000,1≤p≤10,0≤a​i​​,b​i​​<p。
对于测试点 7∼87∼8:1≤n,m≤1000000,p=2,0≤ai,bi<p1≤n,m≤1000000,p=2,0≤a​i​​,b​i​​<p。
对于测试点 9∼209∼20:1≤n,m≤1000000,1≤p≤10,0≤ai,bi<p1≤n,m≤1000000,1≤p≤10,0≤a​i​​,b​i​​<p。

 

可以通过冒泡排序来找逆序对(20分), 还可以通过归并排序来找逆序对(30分),满分就需要找规律了:

理论存在,魔法开始

for(int i = 1;i <= n;i++)
    for(int j = 1;j <= m;j++)
        c[(i-1)*m+j] = (a[i]+b[j])%mod;

i = 1,j = 1:c[0*m+j] = c[1]

        j = 2:c[0*m+j] = c[2]

……

        j = m:c[0*m+j] = c[m]

i = 2,j = 1:c[1*m+j] = c[m+1]

        j = 2:c[1*m+j] = c[m+2]

……

       j = m:c[1*m+j] = c[2m]

i = n,j = m:c[(n-1)*m+j] = c[n*m]

所以它其实是把c分成n份,每份m个

于是就可以分类讨论。

由于他还要%mod,c数组中的元素一定不超过mod,在mod-1和0之间。

我们就可以枚举c[i]前面有多少个在c[i+1]到mod-1的元素这样就能求c[i]的逆序对了。

 

#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位,不然会TLE
            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-IOI吧!!!

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值