E. The Contest
题目链接:点我啊╭(╯^╰)╮
题目大意:
个数的排列,要求分为三堆数字
第一堆数字范围在:
~
第二堆数字范围在:
~
第三堆数字范围在:
~
现全部打乱后
求现在的三堆数字变换到要求需要的最少步数
解题思路:
为第一堆数字里从
到
一共出现了多少个数字
依次类推
如果最后答案每堆数字的范围依次是
~
、
~
、
~
那么就要从第一堆数字里拿出
从第二堆里拿出
从第三堆里拿出
化简后为
我们如果枚举
,只需要查找一个
,满足
且
最小即可,即维护前缀最小
核心:贪心化简
#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 5;
int k1, k2, k3, k[5], c[maxn];
int s1[maxn], s2[maxn], s3[maxn];
int main() {
for(int i=1; i<4; i++) scanf("%d", k+i);
k1 = k[1], k2 = k[2], k3 = k[3];
int n = k1 + k2 + k3;
for(int i=1; i<4; i++)
for(int j=1, num; j<=k[i]; j++)
scanf("%d", &num), c[num] = i;
for(int i=1; i<=n; i++){
s1[i] = s1[i-1] + (c[i] == 1);
s2[i] = s2[i-1] + (c[i] == 2);
s3[i] = s3[i-1] + (c[i] == 3);
}
int ans = n, sav = 0;
for(int i=0; i<=n; i++){
sav = min(sav, s2[i] - s1[i]);
ans = min(ans, sav + s1[n] + s2[n] + s3[i] - s2[i]);
}
printf("%d\n", ans);
}
F. Make Them Similar
题目链接:点我啊╭(╯^╰)╮
题目大意:
个数,现定义两个数相似为两个数二进制
的个数相等
要求
个数异或一个数
后
个数相似
解题思路:
考虑答案为一个
位的数,枚举前
位,折半搜索后
位
问题在于枚举前
位的时候,如果直接记录每个数字异或之后的
的个数
枚举后
位时,不能直接查找到对应的答案
其实想了一下也是可以的,枚举最后相似的位数
~
更好的办法是记录
为前
位每个数字异或之后的
的个数
然后将
放入map中
查找的时候就查
这样就能保证
个数字之间的关系
核心:折半搜索 + 技巧
#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
const int maxn = 1e2 + 5;
int n, a[maxn];
map <vector <int>, int> mp;
int main() {
scanf("%d", &n);
for(int i=1; i<=n; i++) scanf("%d", a+i);
int sum = (1 << 15) - 1;
for(int s=0; s<1<<15; s++){
vector <int> t1, t2;
for(int i=1; i<=n; i++) t1.push_back(__builtin_popcount(a[i] & sum ^ s));
for(int i=1; i<t1.size(); i++) t2.push_back(t1[i] - t1[0]);
mp[t2] = s;
}
sum = (1 << 30) - 1 - sum;
for(int s=0; s<1<<15; s++){
int ts = s << 15;
vector <int> t1, t2;
for(int i=1; i<=n; i++) t1.push_back(__builtin_popcount(a[i] & sum ^ ts));
for(int i=1; i<t1.size(); i++) t2.push_back(t1[0] - t1[i]);
if(mp.count(t2)){
printf("%d\n", mp[t2] + ts);
return 0;
}
}
puts("-1");
}