Educational CF Round 76 (Div. 2)___E —— 思维 F —— 折半搜索

E. The Contest

题目链接:点我啊╭(╯^╰)╮

题目大意:

     n n 个数的排列,要求分为三堆数字
    第一堆数字范围在: 1 1 ~ i i
    第二堆数字范围在: i i ~ j j
    第三堆数字范围在: j j ~ n n
    现全部打乱后
    求现在的三堆数字变换到要求需要的最少步数

解题思路:

     s 1 [ i ] s1[i] 为第一堆数字里从 1 1 i i 一共出现了多少个数字
     s 2 s 3 s2、s3 依次类推
    如果最后答案每堆数字的范围依次是 1 1 ~ i i i i ~ j j j j ~ n n
    那么就要从第一堆数字里拿出 s 1 [ n ] s 1 [ i ] s1[n] - s1[i]
    从第二堆里拿出 s 2 [ i ] + s 2 [ n ] s 2 [ j ] s2[i] + s2[n] - s2[j]
    从第三堆里拿出 s 3 [ j ] s3[j]
    化简后为 s 2 [ i ] s 1 [ i ] + s 1 [ n ] + s 2 [ n ] + s 3 [ j ] s 2 [ j ] s2[i] - s1[i] + s1[n] + s2[n] + s3[j] - s2[j]
    我们如果枚举 j j ,只需要查找一个 i i ,满足 i < j i<j
    且 s 2 [ i ] s 1 [ i ] s2[i] - s1[i] 最小即可,即维护前缀最小

核心:贪心化简

#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

题目链接:点我啊╭(╯^╰)╮

题目大意:

     n n 个数,现定义两个数相似为两个数二进制 1 1 的个数相等
    要求 n n 个数异或一个数 x x
     n n 个数相似

解题思路:

    考虑答案为一个 30 30 位的数,枚举前 15 15 位,折半搜索后 15 15
    问题在于枚举前 15 15 位的时候,如果直接记录每个数字异或之后的 1 1 的个数
    枚举后 15 15 位时,不能直接查找到对应的答案
    其实想了一下也是可以的,枚举最后相似的位数 0 0 ~ 30 30
    更好的办法是记录 l o w [ i ] low[i] 为前 15 15 位每个数字异或之后的 1 1 的个数
    然后将 l o w [ 2 ] l o w [ 1 ] l o w [ 3 ] l o w [ 1 ] . . . . . . l o w [ n ] l o w [ 1 ] low[2] - low[1]、low[3] - low[1]......low[n] - low[1] 放入map中
    查找的时候就查 h i g h [ 1 ] h i g h [ 2 ] h i g h [ 1 ] h i g h [ 3 ] . . . . . . h i g h [ 1 ] h i g h [ n ] high[1] - high[2]、high[1] - high[3]......high[1] - high[n]
    这样就能保证 n n 个数字之间的关系

核心:折半搜索 + 技巧

#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");
}
发布了221 篇原创文章 · 获赞 220 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/Scar_Halo/article/details/103092389