Codeforces Round #829 A+B+C+D 题解

A. The Ultimate Square

题意

询问 \(T\) 次,给定 \(n\) 块木板,第 \(i\) 块为 \(1\times\lceil\frac i2\rceil\) 大小,求能拼出的最大正方形边长

数据范围: \(1\le n\le10^9,1\le T\le10^4\)

题解

\(n\) 为奇数时最大的那块木板放最上面,剩下的最大最小依次两两组合,一定能拼出 \(\lceil\frac i2\rceil\times\lceil\frac i2\rceil\) 的矩形

\(n\) 为偶数时面积最大那块不能用,剩下的就是和奇数一样

综上,答案就是 \(\lceil\frac n2\rceil\),复杂度 \(\Theta(T)\)

Code

#include<bits/stdc++.h>
using namespace std;
signed main()
{
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int n,x;
	cin>>n;
	while(n--)cin>>x,cout<<(x+1)/2<<endl;
	return 0;
}

B. Diverse Substrings

题意

询问 \(T\) 次,每次给定一个长为 \(n\) 的字符串,字符串中仅包含 \(0\)\(9\) 共计十种字符,询问这个字符串有多少个子序列满足其中存在的字符的重复次数都不超过其中不同的字符种类数

数据范围: \(1\le n\le10^5,1\le\sum n\le10^5,1\le T\le10^4\)

题解

乍一看似乎很恶心,实际上非常简单,注意到一共只有十种字符,也就是说每种字符都最多重复 \(10\) 次,合法子序列最大长度就是 \(100\),以每个点为起点一边枚举一边暴力判断即可,总复杂度 \(\Theta(T\times100\sum n)\)

Code

#include<bits/stdc++.h>
using namespace std;
string a;
int n,ans;
int b[15];
inline void Main()
{
	ans=0;
	cin>>n>>a;
	bool ok;
	for(int i=0,cnt;i<n;++i)
	{
		cnt=0;
		for(int i=0;i<10;++i)b[i]=0;
		for(int j=i;j<i+100&&j<n;++j)
		{
			if(++b[a[j]^48]==1)++cnt;
			ok=1;
			for(int k=0;k<10;++k)
                if(b[k]>cnt)ok=0;
			if(ok)++ans;
		}
	}
	cout<<ans<<endl;
}
signed main()
{
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int T;
	cin>>T;
	while(T--)Main();
	return 0;
}

C. Zero-Sum Prefixes

题意

询问 \(T\) 次,每次给定长度为 \(n\) 的序列 \(\{a_n\}\),如果 \(a_i=0\) 你就可以把它换为任意其他数字,求操作完成后的序列最多有多少个前缀和为 \(0\),形式化地讲,你需要使得满足 \(\sum_{k=1}^ia_k\)\(i\) 尽可能的多

数据范围: \(1\le n\le2\times10^5,1\le\sum n\le2\times10^5,1\le T\le10^4\)

题解

更改某个值,只会影响这个位置以及后面位置的前缀和,如果改为 \(c\),相当于给所有后面的前缀和都加上了 \(c\),由于 \(c\) 可以是任意数,所以就相当于可以把这个 \(0\) 到下一个 \(0\) 中间的所有前缀和都可以整体加上一个整数,下一个 \(0\) 可以变为任意整数就能消除这次变化的影响, 要让前缀和为 \(0\) 的最多,也就是问任意两个 \(0\) 之间的众数出现了多少次,开个桶 \(\Theta(n)\) 扫一遍就可以了,总复杂度 \(\Theta(Tn)\)

有几个细节需要注意容易写错,首先是注意统计众数个数的区间是这个零的位置到下一个零的前一个位置,别忘了要记得统计最开头到第一个零的位置的答案还有最后一个零到最后的位置的贡献,如果一个零都没有也要特判

Code

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=2e5+5;
int n,ans;
ll s[N];
vector<int>zero;
map<ll,int>m;
inline void Main()
{
	zero.clear();
	ans=0;
	cin>>n;
	for(int i=1,tmp;i<=n;++i)
	{
		cin>>tmp,s[i]=s[i-1]+tmp;
		if(tmp==0)zero.emplace_back(i);
	}
	if(zero.empty())
	{
		for(int i=1;i<=n;++i)if(s[i]==0)++ans;
		cout<<ans<<endl;
		return;
	}
	for(int i=1;i<zero.front();++i)if(s[i]==0)++ans;
	int mx;
	for(auto x=zero.begin();(*x)!=zero.back();++x)
	{
		mx=0;
		m.clear();
		for(int i=*x;i<(*(x+1));++i)
			mx=max(mx,++m[s[i]]);
		ans+=mx;
	}
	mx=0;
	m.clear();
	for(int i=zero.back();i<=n;++i)
		mx=max(mx,++m[s[i]]);
	ans+=mx;
	cout<<ans<<endl;
}
signed main()
{
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int T;
	cin>>T;
	while(T--)Main();
	return 0;
}

D. ConstructOR

题意

询问 \(T\) 次,每次给定三个正整数 \(a,b,d\),请你求出在 \([0,2^{60})\) 内任意一个满足 \(a|x\)\(b|x\) 都是 \(d\) 的倍数的 \(x\)\(|\) 指按位与运算)

数据范围: \(1\le a,b,d<2^{30},1\le T\le10^4\)

题解

注意到 \(x\) 的二进制位数是 \(a,b,d\) 的两倍,有什么用呢?考虑二进制前 \(30\) 位,由于要求 \(a|x\)\(b|x\) 都是 \(d\) 的倍数,这样很不好搞,所以我们就可以把前 \(30\) 位都先留出来,令 \(x=a|b\),这样 \(a|x=b|x\),但是前 \(30\) 位就被限制了,\(x\) 剩下的二进制位数正好可以留给我们进行调整使得 \(x\)\(d\) 的倍数

首先如果 \(a|b\) 的最低位的 \(1\) 的位置都比 \(d\) 的最低位低,那么显然不可能将 \(d\) 乘上一个数使得更低位出现 \(1\),此时无解

然后考虑怎么构造 \(x\),可以始终保证 \(x\)\(d\) 的倍数再想办法让 \(x=a|x,x=b|x\),从当前答案最低位的 \(1\) 开始向高位考虑,对于一个位置 \(k\) 如果 \(x\)\(1\),而 \(a|b\)\(0\),就说明我们需要再加上一个 \(d\)\(2^{k-m}\) 倍( \(m\)\(d\) 二进制末尾 \(0\) 的个数)来使得这一位变成 \(1\)

可能不太好理解,可以自己手玩两组样例加深理解

总复杂度 \(\Theta(30T)\) (十分不严谨的奇怪表达方式,感性理解一下)

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
int a,b,d,ans;
signed c;
inline signed lowbit(const int& x){return x&-x;}
inline void Main()
{
	cin>>a>>b>>d;
	if(min(lowbit(a),lowbit(b))<lowbit(d))return cout<<"-1\n",void(0);
	ans=0;
	for(signed i=c=__builtin_ctz(d);i<30;++i)
		if((~(ans>>i)&1)&&(((a|b)>>i)&1))
			ans+=(d<<(i-c));
	cout<<ans<<'\n';
}
signed main()
{
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int T;
	cin>>T;
	while(T--)Main();
	return 0;
}	

博客园传送门

知乎传送门

posted @ 2022-11-18 21:24  人形魔芋  阅读(22)  评论(0编辑  收藏  举报