#周测 9 —— 高低位交换、Subsequences Summing to Sevens S、积木大赛、跳石头


很简单的一次周测,先简略记录一下。

周测网站(给在该团队的人看的)

高低位交换

#include<bits/stdc++.h>
using namespace std;
int s[33];
int main(){
	long long x;
	cin >> x;
	for(int i = 32; i >= 1; i --){
		//如果当前最低位上有数 
		if(x % 2 == 1) s[i] = 1;//对应位置为 1
		x >>= 1;//处理完之后就去掉最后一位 
	}
	
	for(int i = 17; i <= 32; i ++){
		x <<= 1;
		x |= s[i];
	} 
	for(int i = 1; i <= 16; i ++){
		x <<= 1;
		x |= s[i];
	} 
	cout << x;
	return 0;
}
/*
//首先来分析一下,这道题很有意思;
//给定一个数 x,  
//然后 按照二进制码, 将它 的前十六位 与 后十六位 交换;
//具体操作:
//一、用另一个数 a 来 复制这个数 x;
//二、将 a往后移16位:
	//a >> 16;
//三、同样的, 用一个数 b 来复制 x , 并将其 往前移16位;
//最后将 a、b相加; 
//不出意料失败了;
//不过没关系,我们分开来做
//首先最最重要的就是后十六位, 因为我们不知道往前移会发生什么东西;
//所以我们先将这个数的二进制位拆分:
	for(int i = 1; i <= 32; i ++){
		//如果当前最低位上有数 
		if(x % 2) s[i] = 1;//对应位置为 1
		x >> 1;//处理完之后就去掉最后一位 
	} 
//现在我们处理完了,下一步就是重新组合这个数了
//第一步,我们将第17到32位的数加上,也就是先把 y 往右移一位,然后 |1(或上1) 
//第二步,我们将第1到16位上的数进行操作 ~ 完成 ~ 
//后来发现 x 取完之后就空了,于是计划好的 y 就变成 x 了;
//为了防止某些 边界问题 我保险起见用long long 吧 
*/

跳石头

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
ll a[N];
int main(){	
	ll L;
	int n, m; 
//	scanf("%d%d%d", &L, &n, &m);//最大距离, 石头数, 最大移走数 
	cin >> L >> n >> m;
	for(int i = 1; i <= n; i ++){
		ll d;
//		scanf("%d", &d);
		cin >> d;
		a[i] = d;
	}
	if(n == 0){
		cout << L;
		return 0;
	}
	//二分答案:使得选手们在比赛过程中的最短跳跃距离尽可能长 我们就枚举 这个——最短的跳跃距离。 
	ll l = 0, r = L + 1, mid;
	int k = 1;
	//现在第一块没有被移走的石头就是1了 
	//k代表的是	 还未被移走的第一块 石头, 于是我们每移走一块石头,k都要向后移一位。 
	int cnt;
	a[n + 1] = L;
	ll res = -1;
	while(l <= r){
		cnt = 0, k = 1;//每一轮都要刷新一次哦。 
		mid = (l + r) / 2;
		for(int i = 0; i <= n;){//为什么从第0块石头开始枚举呢?因为第0块石头就是起点 
			
			ll d = a[k] - a[i]; //d是当前节点到 k 的距离啦	
			while(mid > d && k <= n){//如果 d 小于我们要求的最小距离,那么就把 第k块石头 移开  
				cnt ++;//移开的数目++ 
				k ++;//k往后移 
				d = a[k] - a[i]; //更新 d 的值 
			}	
			//现在这一轮之后,从i开始后面符合距离的石头全部移开了,那么下一步我们就要跳到 k这个位置上了
			i = k;
			k ++;
		}
		if(cnt <= m){//合法 ,增加距离 
			res = max(res, mid);
			l = mid + 1;
		} 
		else {//不合法 ,缩短距离 
			r = mid - 1;
		}
	}	
	cout << res<< endl;
//	printf("%lf MB", (double)sizeof(a) / (1024 * 1024));
	return 0;
}
//什么意思呢?就是说,移走之后, 所有石头之间的距离大于等于这个数 。
//那哪些石头会被移走呢? 
//从第i块石头到第j块石头的距离 d 如果 小于 我们所枚举的这个距离 mid,那就移走第j块石头;
//那移走会怎么样? cnt ++ 
//不移走呢?那就把指针 k 移动到第j块石头上 
//如果 结果 比 能移开的石头数多, 那我们尝试缩小这个距离;注意:这是不合法的,所以它的答案我们一律不会采纳。 
//如果 结果 比 能移开的石头数少, 那就扩大这个距离;注意:此时它的答案是合法的,所以我们会采纳尽量大的数; 
 //如果没有石头可以移动呢???!!! 

积木大赛

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
typedef long long ll;
int a[N], tail;//从1开始 , 所以尾巴在0这里,代表没有数! 
//优先队列 
ll cnt;
int main(){
	int n, x;
	cin >> n;
	for(int i = 1; i <= n; i ++){
		scanf("%d", &x);//当前的数,但不急着加入 
		
		//先判断它与最后一个元素的关系
		//首先是大于等于最后一个元素 
			//那就直接加入就好 
		if(x >= a[tail]){ a[++tail] = x; } 
		
		//然后是小于最后一个元素 
		else{
			//首先将操作数加上
			cnt += (ll)(a[tail] - x);
			//然后将所有前面比他高的全部干掉吧 !(嫉妒心:MAX ) 
			//移动尾巴就好啦,不用枚举;注意不用超出数组范围 
			while(tail > 0 && a[tail] > x) tail --;
			//然后就可以入队啦 
			a[++tail] = x;
		}
	}
	//最后的处理 
	cnt += (ll)a[tail]; 
	cout << cnt;
	return 0;
}
/*
//好形象啊
//似乎可以用优先队列来做
//不过又有所不同,毕竟它是每次只能建一层楼高…… 
//对了!!!如果本次输入导致那一层的延续断掉了,那么cnt++不久好了吗?

//分两种情况,
//第一种: 这次输入的是比优先队列最后一个元素大的
//直接加入吧没什么大不了的 
//第二种: 这次输入的是比优先队列最后一个元素小的 
//这就有意思了,我们要将 最后一个元素 到 当前元素中间的所有楼层都 “建好”。
//也就是操作数为 两者之差;
cnt += (a[tail] - x);
//第三种,最后的处理;
//最后剩下的怎么办??
//最简单了,直接加上最后一个元素就可以了!!!
cnt += a[tail]; 
//cnt会超出int 的范围吗??
//cnt最大会是多少呢??
//假设有一个很邪恶的数据,它每次都卡范围,按照10000、1、10000、1……的顺序来建房子
//这样的组合最多有50000个,每一组侧次数是9999(算它10000)
//那么我们的cnt最大就是 500000000 , 9位数啦!!!
//所以以防万一,我们开long long ! 
*/

Subsequences Summing to Sevens S

//Subsequences Summing to Sevens S
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int a[N], s[N], b[7];
//b是那个数第一次出现在的位置 
int res = -1;
int main(){
	memset(b, -1, sizeof b);
	int n;
	cin >> n;
	for(int i = 1; i <= n; i ++){
		scanf("%d", &a[i]);
		a[i] %= 7;
		s[i] = (a[i] + s[i - 1]) % 7;
		if(b[a[i]] == -1) b[a[i]] = i;//没有出现过就更新位置 
	}
	//前缀和 %7
	
	for(int i = 1; i <= n; i ++)
		if(s[i] == 0) res = max(res, i);
		else if(b[s[i]] != -1 && b[s[i]] < i)//如果这个数存在 
			res = max(res, i - b[s[i]]);
	
	cout << res;
	return 0;
}
//前缀和做法,
//首先开一个数组a,将所有输入的数取模  
//再开一个前缀和数组s,将所有前缀和对7取模
//再开一个数组b,用来记录0~6第一次分别出现的位置
//然后遍历前缀和数组,如果上面对7取模的结果存在,那么就用当前位置i减去s[i]第一次出现的位置就可以了。
//或者已经为0,也就是前缀和本身是7的倍数,那此时长度就是i;
//比较最大的结果res;
原文地址:https://www.cnblogs.com/yuanyulin/p/14026727.html