CSP-J 模拟赛4

分数

T1:三个(three)0'(100

T2:合体(fit)0'(70

T3:矩阵(matrix)0'(0

T4:数对(pair)0'(5

总分:0'(175)     

注:文件错误,freopen里w和r写反爆零了,括号里是重判分。

题解

T1:三个(three)

题面:

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

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

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

赛时总结:

一眼,稳定发挥。

思路:

按规律O(n)暴力求解每一秒的生物数,注意一点,每一个被分裂出的生物也能分裂,要边运算边取模,不然会爆int(开long long也不行)。

AC代码:

#include<bits/stdc++.h>
using namespace std;
int n;
int main(){
	cin>>n;
	int a=1,b=1,c=1;
	while(n--){
		int x=a,y=b,z=c;
		a=(x+x+2*y+z)%2,b=(y+x+z)%2,c=(z+x+2*y)%2;
	}
	if(a%2==1)	cout<<"odd\n";
	else cout<<"even\n";
	if(b%2==1)	cout<<"odd\n";
	else cout<<"even\n";
	if(c%2==1)	cout<<"odd\n";
	else cout<<"even\n";
	return 0;
}

T2:合体(fit)

题面:

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

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

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

赛时总结:

下次记得关同步。

思路:

可以预处理所有的数,但O(n^{2})肯定是会爆TLE的,所以可以写个小递推

设状态:f(i)=\left \lfloor \frac{f(i-1)}{2} \right \rfloor

然后带入算就行。

AC代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,a[1000005],q,t[1000005],f[1000005];
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		t[a[i]]++;
	}
	f[1]=t[1];
	for(int i=1;i<=m;i++){
		f[i]=f[i-1]/2+t[i];
	}
	cin>>q;
	while(q--){
		int x;
		cin>>x;
		cout<<f[x]<<'\n';
	}
	return 0;
}

T3:矩阵(matrix)

题面:

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

一组数字a_{1},a_{2},...,a_{n}的异或和为a_{1}\bigoplus a_{2}\bigoplus ... \bigoplus a_{n}​​。(其中\bigoplus表示按位异或运算)

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

赛时总结:

打了好久O(n^{6})的大模拟,还是写炸了。

思路:

考虑将二维数组压成一维的
枚举起始行i和终止行j,这个范围内的每一列都求异或值(例如x[k]a[i][k] \sim a[j][k]的异或值)。之后再对于x数组求前缀异或值,然后枚举其左右端点,计算区间异或值即可。

但是这样的时间复杂度为O(n^{4}),还是会爆掉。

对于上述方案,枚举完起始行和终止行之后,还需要O(n^{2})去计算结果,这一部分可以考虑优化。

按位去考虑,对于某个区间,按位异或之后的值的某一位,是否为1。只需要考虑这个区间内的这一位的1的个数是奇数个还是偶数个。

也可以考虑sum数组 ans = \sum_{l=1}^{r} sum[r] xor sum[l - 1],按位考虑,某一位如果想被累计到答案里,那么sum[r]的这一位和sum[l-1]的这一位就不相同。所以可以考虑把sum拆位,如果当前这一位是1,那么就考虑它前面这一位是0的情况有多少个,反之同理。

AC代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=305;
int n,m,a[N][N],b[N],sum[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 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;
	return 0;
}

T4:数对(pair)

题面:

给你一个长度为n的数列a_1,a_2,...a_n
再给你一个长度为m的数列b_1,b_2,...,b_m
现在再再再给你一个正整数p,让你生成一个长度为nm的数列c_1,c_2,...,c_{nm}​​。
其中满足 c_{(i-1)m+j}=(a_i+b_j)\mod p
现在问你数列c中有多少个数对(i,j)满足i<jc_i>c_j

赛时总结:

没看懂,直接输出0。

思路:

我们观察到c数组是可以分成n块,每一块有m个数字。 可以对块内求逆序,然后再块与块之间求。

对于块内
观察到值域非常小,我们可以对b数组记录数字出现次数。
枚举到当前数字j,计算逆序数时,可以枚举比j大的数字的出现次数即可。也就是记录 \sum_{b[k+1]}^{p-1} vis[i]
考虑b数组后续需要变化。所有我们记录一个数组res[k]。表示为对于b数组所有数组都加k之后,逆序数为多少。

对于块与块
我们可以记录之前所有数字的出现次数,当前块一定是在之前块的后面,直接枚举值域,统计逆序数即可。

AC代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e6+5;
int n,m,p,a[N],b[N];
ll cntb[11],res[11],vis[11];
void write(__int128 x){
	if(x>9) write(x/10);
	putchar(char(x%10+'0'));
}
int main(){
	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;
}

总结

服了,文件还能写错 。

这次爆零了,也是模拟首爆,下次记得别把r和w写反了,第二题也就差一点,被卡了cin、cout,下次记得关同步,有史以来因为个人疏漏出错最多的一次,希望下次能吸取教训吧。

早日AKJ组!!!

最后把文件输入输出贴在这里

freopen("xxx.in","r",stdin);
freopen("xxx.out","w",stdout);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值