Codeforces Round #833 (Div.2) A-D

A. The Ultimate Square

题目大意:有 n n n 个宽度为 1 1 1 的小长方形,长度为 1 , 1 , 2 , 2 , 3 , 3...... 1,1,2,2,3,3...... 1,1,2,2,3,3...... 。问用这些长方形(不可旋转)能组成的正方形的最大边长。

说实话这题看样例就能看出来答案……我们还是浅浅的证明一下吧。

n n n 为奇数,则有 1 × 1 1\times 1 1×1 ~ ( n − 1 ) / 2 (n-1)/2 (n1)/2 的长方形各两个,以及一个 1 × ( n + 1 ) / 2 1\times (n+1)/2 1×(n+1)/2 的长方形。将 1 1 1 ( n − 1 ) / 2 (n-1)/2 (n1)/2 配对, 2 2 2 ( n − 1 ) / 2 − 1 (n-1)/2-1 (n1)/21 配对…… ( n − 1 ) / 2 (n-1)/2 (n1)/2 1 1 1 配对,则可以组成一个 ( n + 1 ) / 2 × ( n − 1 ) / 2 (n+1)/2\times (n-1)/2 (n+1)/2×(n1)/2 的长方形。再加上 1 × ( n + 1 ) / 2 1\times (n+1)/2 1×(n+1)/2 的长方形即可正好配成一个 ( n + 1 ) / 2 × ( n + 1 ) / 2 (n+1)/2\times (n+1)/2 (n+1)/2×(n+1)/2 的正方形。

n n n 为偶数,则单独拿出来一个 n / 2 n/2 n/2 的长方形,剩下的长方形组成一个 n / 2 × n / 2 n/2\times n/2 n/2×n/2 的正方形。此时,由于 n / 2 + ( n / 2 ) 2 = ( n / 2 ) ( n / 2 + 1 ) ≤ ( n / 2 + 1 ) 2 n/2+(n/2)^2=(n/2)(n/2+1)\le(n/2+1)^2 n/2+(n/2)2=(n/2)(n/2+1)(n/2+1)2 ,故不可能形成一个更大的正方形。于是,当 n n n 为偶数,答案为 n / 2 n/2 n/2

于是这题就解决了。

#include<bits/stdc++.h>
using namespace std;
int t,n;
int main(){
	cin>>t;
	while(t--){
		cin>>n;
		cout<<(n+1)/2<<endl;
	}
	return 0;
}

B. Diverse Substrings

题目大意:定义一个合法数字串为:其出现次数最多的数字出现次数不超过数字种数。给定长度为 n n n 的只含数字的字符串,求其合法的连续字串数。

我拿到这题的第一个想法是类似数列找不同一题的做法,但发现完全套不上去,后来自己想没想到什么靠谱的做法,于是打了个 n 2 n^2 n2 暴力,第一层枚举长度,第二层枚举左端点。后来想一想,发现长度最多为 100 100 100 (否则,由于数字总数只有 10 10 10 ,若长度超出 100 100 100 ,则根据抽屉原理,必然有至少一种数的出现次数 ≥ 10 \ge 10 10 ,也就必然不合法)。这样枚举量就大大减小,可以较稳地通过此题。

#include<bits/stdc++.h>
using namespace std;
int t,n,a[100010],vh[10],kd,mx,ans=0;
string st;
int main(){
	cin>>t;
	while(t--){
		ans=0;
		cin>>n;
		cin>>st;
		for(int i=0;i<n;++i) a[i+1]=st[i]-'0';
		for(int i=1;i<=n;++i){
			kd=0,mx=0;
			memset(vh,0,sizeof(vh));
			for(int len=1;len<=100;++len){
				int j=i+len-1;
				if(j>n) continue;
				if(!vh[a[j]]) kd++;
				vh[a[j]]++;
				mx=max(vh[a[j]],mx);
				if(mx<=kd) ans++;
			}
		}
		cout<<ans<<endl;
	}
	return 0;
}

C. Zero-Sum Prefixes

题目大意:给定数组 a a a ,你可以通过将其中数值为 0 0 0 的位置变成另一个数字(任意多次)。求更改后的 a a a 数组的前缀和数组 s s s 0 0 0 最多有多少个。

我们可以将整个数组按照 0 0 0 的出现位置将整个数组划成几段,然后做前缀和。对于每一段的前缀和数组,我们相当于可以将它们同时加上(或减去)一个数(通过改变 0 0 0 ),而且不会影响后面的前缀和数组各个数之间的相对差。于是,问题转化成:对于每一段前缀和数组,求其出现次数最多的数的出现次数。这里要注意:对于出现 0 0 0 之前的那一小段,别忘了另作处理。

#include<bits/stdc++.h>
using namespace std;
long long t,n,a[200010],s[200010],p[200010],pos,ans;
string st;
map<long long,long long>m;
int main(){
	cin>>t;
	while(t--){
		cin>>n;
		pos=0,ans=0;
		for(long long i=1;i<=n;++i) cin>>a[i];
		for(long long i=1;i<=n;++i){
			s[i]=s[i-1]+a[i];
			if(!a[i]) p[++pos]=i;
		}
		p[++pos]=n+1;
		for(long long i=1;i<pos;++i){
			m.clear();
			long long mx=0;
			for(long long j=p[i];j<p[i+1];++j){
				m[s[j]]++;
				mx=max(mx,m[s[j]]);
			}
			ans+=mx;
		}
		for(long long i=1;i<p[1];++i) if(!s[i]) ans++;
		cout<<ans<<endl;
	}
	return 0;
}

D. ConstructOR

给定正整数 a , b , d < 2 30 a,b,d< 2^{30} a,b,d<230 ,求任意一个整数 x < 2 60 x< 2^{60} x<260 ,使得 d ∣ ( a   o r   x ) , d ∣ ( b   o r   x ) d|(a\space or\space x),d|(b\space or\space x) d(a or x),d(b or x) ∣ | 为整除, o r or or 为或运算)。有至多 10000 10000 10000 组数据。

考场上一直在往 d p dp dp x x x 上面想,没想出来,后来听了老师的讲解恍然大悟。

老师的思路是:枚举 d d d 的倍数,直到 a & ( d × k ) = a , b & ( d × k ) = b a\&(d\times k)=a,b\&(d\times k)=b a&(d×k)=a,b&(d×k)=b ,此时的 d × k d\times k d×k 即为问题的一个解(不难证明)。但是显然这会超时,我们要想着优化。由于要求的是 a & ( d × k ) = a , b & ( d × k ) = b a\&(d\times k)=a,b\&(d\times k)=b a&(d×k)=a,b&(d×k)=b ,相当于 ( a (a (a| b ) & ( d × k ) = ( a b)\&(d\times k)=(a b)&(d×k)=(a| b ) b) b)。于是乎,当 d d d 的二进制形式末尾的 0 0 0 数量大于 ( a (a (a| b ) b) b) 二进制形式末尾 0 0 0 的数量,此必无解。否则,将两者的结尾一起删除 0 0 0 ,直到 d d d 后面没 0 0 0 了。然后,按二进制位枚举,如果此时的枚举的位 d × k d\times k d×k a a a| b b b 不同,那么 k k k 就要加上 1 < < i 1<<i 1<<i (因为删 0 0 0 后的 d d d 最后一位是 1 1 1 ,加上 d < < i d<<i d<<i 后得到的数的第 i i i 位一定会发生变化)。这样一直推就得出正解了。

我讲得不是很清楚(太抽象+本人蒟蒻),结合代码理解理解吧。

#include<bits/stdc++.h>
using namespace std;
unsigned long long a,b,t,d,s;
int main(){
	cin>>t;
	while(t--){
		s=0;
		cin>>a>>b>>d;
		unsigned long long x=a|b;
		if(__builtin_ctz(x)<__builtin_ctz(d)){
			cout<<-1<<endl;
			continue;
		}
		unsigned long long ed=__builtin_ctz(d);
		while(!(d%2)){
			x/=2,d/=2;
		}
		for(unsigned long long i=0;i<=30;++i){
			if((s&x)==x) break;
			if(__builtin_ctz(s^x)<=i){
				s+=(d<<i);
			}
		}
		unsigned long long ans=(s<<ed);
		cout<<ans<<endl;
	}
	return 0;
}

__builtin_ctz就是求某个数二进制下末尾0的个数。__buitin这一类函数还是很好用的(比如用在倍增lca上比枚举二进制位快不少),建议学学。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值