AtCoder Regular Contest 146

因为我太菜了,所以 D 还没写。(明天和ABC一起补)

A - Three Cards

整场比赛最简单的题,很明显要选最大的 \(3\) 个数,但这答案不一定是这 \(3\) 个数从大到小排列,例如现在有 \(21,100,3\),那么很明显 \(321100\) 是要大于 \(100213\) 的,所以求出这 \(3\) 个数的排列再比较大小即可。

upd:不要使用 log10 函数,你可以去试试误差有多大。

B - Plus and AND

整场比赛唯二可做的题。

以下的某一位指该数二进制下从右至左数的位置,注意最右边的那一位是第 \(0\) 位,第 \(i\) 为对这个数的贡献是 \(2^i\)

考虑答案的每一位可否为 \(1\),题目要求最大的答案,因此从高位到低位依次 check,如果第 \(i\) 位符合要求,那么答案就加上 \(2^i\),根据贪心,显然这样是最优的。(注意到 \(2^k>1+2+...+2^{k-1}\),也就是说第 \(k\) 位为 \(1\) 比剩下的 \(k\) 位均为 \(1\) 更优)

假设现在考虑到了第 \(i\) 位,第 \(i\) 位之前的最优答案为 \(ans\),现在检查能否凑出 \(ans+2^i\),记 \(x=ans+2^i\),同时记 \(now(now\ge a_j)\) 表示 \(a_j\) 通过最少次数的加 \(1\) 要变成 \(now\) 才有 \(x \operatorname{and} now=x\),注意到 \(x \operatorname{and} now\le \min(x,now)\),并且当 \(x \operatorname{and} now<x\)(注意到如果 \(now<x\) 时一定满足) 时,在选择的 \(k\) 个数中一旦选择了 \(now\) 这个数,答案就会小于 \(x\),肯定不能拼出 \(x\),所以对于每个 \(a_j\),一定要变成 \(now\) 使得 \(x \operatorname{and} now=x\),才能拼出 \(x\)

先不考虑求 \(now\) ,假设现在已经求出来每个 \(a_j\) 对应的 \(now\) 了,那么记 \(w_j=now-a_j\) 表示需要几个操作,很明显,最优的方案是选 \(w_j\)\(k\) 小的操作数(注意到此时任意 \(k\) 个数都满足与起来等于 \(x\),但最多只能使用 \(m\) 次加 \(1\) 操作),如果此时前 \(k\) 小的 \(w_j\) 的和仍然大于 \(m\),那么 \(x\) 就无法凑出,然后考虑下一位即 \(ans+2^{i-1}\),相反,如果小于等于 \(m\) ,代表 \(x\) 可以凑出,更新 \(ans=x\) 再考虑下一位。

那么如何求 \(now\) 呢?考虑枚举 \(x\) 二进制下的每一位(同上,依旧从高到低位),假设现在看到了第 \(s\) 位,如果第 \(s\) 位为 \(1\),那么 \(now\) 的第 \(s\) 位一定也要为 \(1\),如果第 \(s\) 位为 \(0\),那么 \(now\) 的第 \(s\) 位既可以为 \(0\) 也可以为 \(1\),但要求 \(now-a_j\) 最小,并且 \(now\ge a_j\),所以如果剩下的位置可以使 \(now\ge a_j\),那么 \(now\) 的这一位为 \(1\),否则为 \(0\)

剩下的位置如果可以使 \(now\ge a_j\),那么只要考虑 \(now\) 的最大取值能否满足即可,很明显,\(now\) 最大的情况是 \(0\)\(s-1\) 位均为 \(1\)(第 \(s \) 位不选为 \(0\)),此时 \(now'=now|(2^s-1)\) ,因为 \(2^s-1\) 就是 \(0\)\(s-1\) 位均为 \(1\),其他位为 \(0\)

然后就做完了,时间复杂度大约是为 \(O(n\log^2a)\) ,既然它有 \(5s\) 的时限就轻轻松松了,代码也很简单。

Code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+7;
int a[N],w[N],n,m,k,ans;
bool check(int x)
{
	int sum=0;
	for(int i=1;i<=n;i++)
	{
		int now=x;
		for(int j=31;j>=0;j--)
		if((!(x>>j&1))&&(now|((1ll<<j)-1))<a[i])
			now+=(1ll<<j);
		w[i]=now-a[i];
	}
	sort(w+1,w+n+1);
	for(int i=1;i<=k;i++)
	sum+=w[i];
	return sum<=m; 
}
main() 
{
    scanf("%lld%lld%lld",&n,&m,&k);
    for(int i=1;i<=n;i++)
    scanf("%lld",&a[i]);
    for(int i=31;i>=0;i--)
    {
    	if(check(ans+(1ll<<i)))
    	ans+=(1ll<<i);
	}
	printf("%lld",ans);
	return 0;
}

C - Even XOR

哪次 AT 不考位运算考套路题我请出题人抽烟。

下面所说的偶子集指大小为偶数的子集,奇子集指大小为奇数的子集。

首先我们来看对于一个合法的集合 \(S\) 我们可以加入什么数使得它依旧合法。

由题意可知,对于 \(S\) 的任何一个偶子集加上一个数都满足条件,对与 \(S\) 的任何一个奇子集而言,设它所有数异或起来等于 \(x\) ,那么新加入的数 \(d\) 应该满足 \(x \operatorname{xor} d\not=0\),对于 \(x=d\) 显然不成立,对于 \(x\not =d\) 一定有 \(x\)\(d\) 的二进制下某一位不相同(注意到异或的结果的二进制每一位只与这两个数二进制下对应的位置是否相同有关),也就是说异或的结果肯定不为 \(0\)

也就是说,对于 \(S\) 的某个奇子集,都有一个对应的数不合法,注意到任意连个奇子集的异或值不可能相等,否则这两个奇子集组成的偶子集是不符合要求的(因为一个数异或它自己等于 \(0\)),所以不合法的数和每个奇子集是一一对应的,没有相同的,那么对于一个满足条件的大小为 \(i\) 集合 \(S\) 来说,可以加上 \(2^n-\frac {2^i} 2=2^n-2^{i-1}\) 个数字使得条件仍成立。

然后就没了吗?你会发现样例都过不了,很明显,每个增加的数字在之前和这次已经计算过 \(i\) 次了,那么答案应该要除以 \(i\),具体的可以用递推来实现,设 \(f[i]\) 表示 \(S\) 的大小为 \(i\) 时的答案,那么有:

\[f[i]=\frac {f[i-1]*(2^n-2^{(i-1)-1})} i=\frac {f[i-1]*(2^n-2^{i-2})} i \]

初值 \(f[1]=2^n\),时间复杂度为 \(O(n)\),代码很简单就不放了。

posted @ 2022-09-13 14:43  xiaoPanda  阅读(111)  评论(0)    收藏  举报