周周结——一

  1. Lucky Numbers
    在这里插入图片描述
    题目大意:
    给定数字位数,即这个数的位数要小于或者等于它,且这个数字只能由7和8组成,问有多少种情况。
    分析:
    n位,每一位都有2种选择,2^n
    n-1位,每一位,2^(n-1)

    1位 2^1
#include <bits/stdc++.h>
using namespace std;
int n;
typedef long long ll;
ll qmi(ll a,int k){
	ll res=1;
	while(k){
		if(k&1) res*=a;
		k>>=1;
		a=(ll)a*a;
	}
	return res;
} 
int main()
{
	cin>>n;
	ll ans=0;
	for(int i=n;i>=1;i--) ans+=qmi((ll)2,i);
	cout<<ans<<endl;
	return 0;
} 

  1. Tea with Tangerines
    在这里插入图片描述
    题目大意:
    有 n 块橘子皮,每块大小是a[i]。你可以做一次操作将一块橘子皮分成任意大小的两块,整个过程橘子皮总量是不变的。问要使任意两块橘子皮 ,x,y (x≤y) 都满足 2x>y 的最小操作数。
    分析:
    为了满足分割后每块皮都小于最小块 minsize 的两倍,我们只需将每块皮分割成若干个(minsize×2−1) 即可。
    真正分的时候如果按这种思路分出了更小的皮,只需从任意另两块上取一部分给它即可,所以不会影响最终答案。
    分成块数是a[i]/(minsize*2-1)上取整,操作数为块数-1
#include <bits/stdc++.h>
using namespace std;
const int N=210;
int a[N];
int t;
int n;
int main()
{
	cin>>t;
	while(t--){
		cin>>n;
		int minnum=1e9;
		for(int i=0;i<n;i++){
			cin>>a[i];
			minnum=min(minnum,a[i]);
		}
		int flag=minnum*2-1;//根据最小的块,得到至少要分成这么大小
		//每一块/size上取整=要分成多少块,那么分割次数就是块数-1 
		//上取整 
		int ans=0;
		for(int i=0;i<n;i++) ans+=ceil(a[i]*1.0/flag)-1;
		cout<<ans<<endl;
	}
	return 0;
}
  1. Counting Orders
    在这里插入图片描述
    题目大意:
    求有多少种重新排列a的方式,使得对于任意1=<i<=n,都满足a[i]>b[i],结果对10^9+7取模
    分析:
    朴素想法:对于每一个b中的数,找a中比其大的个数乘起来
    思考问题:在选择某些数的时候考虑其他位置的情况
    首先,我们对数组 b 进行排序,因为它不会改变答案。
    尝试从 a 的第 n 个元素 an 开始选择值,逐渐向前选择 a1。对于 ai 的选择有多少种方式?

新选择的 ai 必须满足 ai > bi。但是,一些候选值已经被选择为 aj(其中 j > i)。由于 aj > bj ≥ bi,我们知道对于所有 j > i 的值,已经有 (n-i) 个候选值被先前选择了。因此,选择 ai 的方式有 (满足 ak > bi 的 k 的数量) - (n-i) 种。

我们可以使用两个指针或二分查找来高效地找到对于每个 i,满足 ak > bi 的 k 的数量。

#include <bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
typedef long long ll; 
int t;
int n;
int main()
{
	cin>>t;
	while(t--){
		cin>>n;
		vector<int> a(n);
		for(int i=0;i<n;i++) cin>>a[i];
		sort(a.begin(),a.end());
		vector<int> b(n);
		for(int i=0;i<n;i++) cin>>b[i];
		sort(b.begin(),b.end(),greater<int>());
		ll ans=1;
		for(int i=0;i<n;i++){
			int temp=a.size()-(upper_bound(a.begin(),a.end(),b[i])-a.begin());
			ans=ans*max(temp-i,0)%mod;
		}
		cout<<ans<<endl;
	}
	return 0;
}
  1. Mex Master
    在这里插入图片描述
    题目大意:给定长度为n的序列a,规定a的权值为mex{a1+a2,a2+a3,…}(mex是指一个非负整数序列中最小的未在序列中出现的整数)。现在a可以任意排列,求a的最小权值
    分析:
    分情况讨论
    1,全是0,答案1
    2,0的数量小于等于n/2,将非零和零组合,答案0
    3,0的数量大于n/2:(1)0的数量和1的数量等于n,0的数量大于1的数量,答案2;(2)答案1
#include <bits/stdc++.h>
using namespace std;
int t;
int n;
int main()
{
	cin>>t;
	while(t--){
		cin>>n;
		int x;
		int cnt0=0,cnt1=0;
		for(int i=0;i<n;i++){
			cin>>x;
			if(x==0) cnt0++;
			if(x==1) cnt1++;
		}
		//如果全是0,1
		//如果一半或者更少是0,组合成非零,0
		//如果不是0就是1,并且0的数量大于1,2
		//如果0的数量大于一半,1 
		int b=(n+1)/2;
		if(cnt0==n) puts("1");
		else if(cnt0<=b) puts("0");
		else if(cnt0+cnt1==n) puts("2");
		else puts("1");
	}
	return 0;
}
  1. Come Together
    在这里插入图片描述
    题目大意:
    给定无限大的网格图中 A,B,C 三点的横纵坐标,你需要求出,从 A 分别到 B 与 C 的最短路径最大有多少格点重合。
    分析:
    网格寻路,四个方向上下左右,也就是考虑A点和B点与C点的横纵坐标的关系
    如果同向,选择两者之间较小的距离
#include<bits/stdc++.h>
using namespace std;
int t;
int ax,ay,bx,by,cx,cy;
int main()
{
	cin>>t;
	while(t--){
		cin>>ax>>ay>>bx>>by>>cx>>cy;
		int dx1=bx-ax,dx2=cx-ax;//从A点到B,C点横向改变量
		int dy1=by-ay,dy2=cy-ay;
		int dx=0,dy=0;
		if(dx1>0&&dx2>0||dx1<0&&dx2<0) dx=min(abs(dx1),abs(dx2));
		if(dy1>0&&dy2>0||dy1<0&&dy2<0) dy=min(abs(dy1),abs(dy2));
		cout<<dx+dy+1<<endl; 
	}
	return 0;
}
  1. Subsequence Addition (Easy Version)
    在这里插入图片描述
    题目大意:
    本题为简单版,两题的唯一区别在于数据范围的大小。

数列 a 最开始只有一个数 1,你可以进行若干次操作,每次操作你可以选取 k 个数(k 无限制,小于等于 a 的大小即可),将这 k 个数的和放入 a 的任意一个位置。
给定一个长度为 n 的序列 c,问 a 能否在进行若干次操作后转为c。
分析:
简单模拟,开始总是1,1,2,3,…形成固定模式
将序列c从小到大进行排序,判断首位是否为1,不为1显然无法从a获得
初步检查后将bitset下标1置为1,其余置为0
从小到大枚举c中元素,判断其在bitset中的对应位是1,若不为1说明无法从a序列得到c
若值为1,需要将所有能够拼出的值加上这个数,此时可以拼出的值下标为1,其余为0
从小到大枚举c中元素,若当前枚举元素为c[i],将bitset与其自身左移c[i]位后的值进行按位或运算即可更新所有能够被被拼出的数
模拟过程:
假设数组 c 的值为 [1, 2, 3]:

初始时,位向量 bitset 为 00000000。

首先,枚举数组 c 的第一个元素 c[0] = 1。

将 bitset 左移 1 位后得到 00000000,与原来的 bitset 按位或运算,得到新的 bitset = 00000001。

接下来,枚举数组 c 的第二个元素 c[1] = 2。

将 bitset 左移 2 位后得到 00000010,与原来的 bitset 按位或运算,得到新的 bitset = 00000011。

最后,枚举数组 c 的第三个元素 c[2] = 3。

将 bitset 左移 3 位后得到 00001100,与原来的 bitset 按位或运算,得到新的 bitset = 00001111。

最终的位向量 bitset 为 00001111,表示我们能够拼出的数为 1, 2, 3。

知识补充:
转载知乎
std::bitset 是 C++ 标准库中的一个类,用于表示二进制位序列。它提供了一种方便的方式来处理二进制数据,尤其适用于位运算操作。

std::bitset 类型表示一个固定长度的位序列,每个位都只能是 0 或 1。这个固定长度在创建对象时指定,并且不能在运行时更改。类似于整数类型,std::bitset 支持多种操作,包括位运算、位查询和位设置。

下面是 std::bitset 类型的创建方式:

 #include <bitset>
 
 std::bitset<N> bitset1; // 创建一个长度为 N 的 bitset,所有位都被初始化为 0
 std::bitset<N> bitset2(value); // 使用二进制整数 value 初始化一个长度为 N 的 bitset
 std::bitset<N> bitset3(string); // 使用二进制字符串 string 初始化一个长度为 N 的 bitset
 std::bitset<N> bitset4(bitset); // 使用另一个 bitset 初始化一个长度为 N 的 bitset
 

其中,value 是一个无符号整数,string 是一个只包含 '0''1' 的字符串,bitset 是另一个 std::bitset 对象。

下面是 std::bitset 类型的一些常用操作:

  • size() 返回 std::bitset 的长度
  • count() 返回 std::bitset 中值为 1 的位的数量
  • any() 返回 std::bitset 中是否存在值为 1 的位
  • none() 返回 std::bitset 中是否所有位都是 0
  • all() 返回 std::bitset 中是否所有位都是 1
  • test(pos) 返回 std::bitset 中位于 pos 位置的值
  • set(pos)std::bitset 中位于 pos 位置的值设为 1
  • reset(pos)std::bitset 中位于 pos 位置的值设为 0
  • flip(pos)std::bitset 中位于 pos 位置的值取反
  • to_ulong() 返回 std::bitset 转换成的无符号整数值
  • to_ullong() 返回 std::bitset 转换成的无符号长整数值

std::bitset 重载了许多二进制运算符,如 &|^~ 等,使其支持类似于整数类型的位运算操作。例如:

 std::bitset<4> bitset1("1010");
 std::bitset<4> bitset2("0110");
 
 std::bitset<4> bitset3 = bitset1 & bitset2; // 按位与运算
 std::bitset<4> bitset4 = bitset1 | bitset2; // 按位或运算
 std::bitset<4> bitset5 = bitset1 ^ bitset2; // 按位异或运算
 std::bitset<4> bitset6 = ~bitset
 

还可以使用左移、右移运算符进行位移操作:

 std::bitset<4> bitset1("0101");
 
 std::bitset<4> bitset2 = bitset1 << 2; // 左移 2 位,结果为 "010100"
 std::bitset<4> bitset3 = bitset1 >> 1; // 右移 1 位,结果为 "0010"
 

std::bitset 还支持 to_string() 方法,将其转换成二进制字符串表示:

 std::bitset<4> bitset1("1010");
 std::string str = bitset1.to_string(); // "1010"
 

std::bitset 可以作为容器类型使用,可以使用下标访问、迭代器等方式访问其元素。此外,它还可以通过位集合(bitset set operations)进行集合运算,如并集、交集、补集等,可以使用 std::bitset 的成员函数 set()reset()flip() 进行相应的集合操作。

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int t,n;
bitset<N> s;
int c[N];
int main()
{
	scanf("%d",&t);
	while(t--){
		s.reset();
		scanf("%d",&n); 
		for(int i=1;i<=n;i++) scanf("%d",&c[i]);
		sort(c+1,c+n+1);
		s[1]=1;
		if(c[1]!=1) cout<<"NO"<<endl;
		else{
			bool flag=true;
			for(int i=2;i<=n;i++){
				if(s[c[i]]) s|=s<<c[i];
				else{
					flag=false;
					break;
				}
			} 
			if(flag) puts("YES");
			else puts("NO");
		}
	}
	return 0;
}

  1. Subsequence Addition (Hard Version)
    数据范围在这里插入图片描述
    分析:
    假设我们有一个通过一系列操作生成的数组a,其中元素的总和为s。我们想要证明,在数组a中添加任何介于1和s之间的数x(1≤x≤s),我们仍然可以使用改变后的数组a来生成任何介于1和s+x之间的数。

我们首先假设在长度为l的数组a中添加了数x(1≤x≤s)。根据题设中的操作规则,我们可以使用原始数组a的元素来生成任何介于1和s之间的数b。

现在我们考虑使用元素x和原始数组a的某个子集来生成目标数b’,其中b满足x≤b≤s+x。

如果目标数b’小于或等于s,我们可以使用原始数组a的元素来生成它,因为我们已经证明了原始数组a可以生成介于1和s之间的任何数。

如果目标数b’大于s,我们可以将其表达为b’=s+k,其中k>0。我们需要注意到,我们可以使用原始数组a的某个子集来生成一个数k,因为原始数组a的元素总和是s。

综上所述,我们可以通过原始数组a的元素和新添加的元素x来生成介于1和s+x之间的任何数。也就是说,对于长度为l+1的新数组,我们仍然可以生成介于1和s+x之间的任何数。

由于上述论证适用于初始数组a,我们可以使用归纳法以及这个结论来证明对于所有的数组都成立。因此,我们只需要验证数组是否满足这个条件。我们可以对数组进行排序,并对每个i(2≤i≤n)检查是否满足ci≤∑i−1j=1cj的条件。

#include <bits/stdc++.h>
using namespace std;
int t;
const int N=2e5+10;
int n;
int main()
{
	cin>>t;
	while(t--){
		cin>>n;
		vector<int> a(n);
		for(int i=0;i<n;i++) cin>>a[i];
		sort(a.begin(),a.end());
		if(a[0]!=1) cout<<"NO"<<endl;
		else{
			bool flag=true;
			long long sum=a[0];
			for(int i=1;i<n;i++){
				if(sum<a[i]){
					flag=false;
					break;
				}
				sum+=a[i];
			}
			if(flag) puts("YES");
			else puts("NO");
		}
	}
	return 0;
} 
  1. Hamon Odyssey
    在这里插入图片描述
    题目大意:
    满足&值最小的的情况下,尽可能多的分组
    分析:
    首先明白&值不增,全部化为一组,&值一定满足最小
    根据样例出思路
    如果某段出现了&值为0的情况,可以进行分组
    最后考虑这样分组最后剩下的&值和全部化为一组的&值的大小情况,如果分组的&值大于一组的&值,将最后的一组归到&值为零的组中(A&0=0),在和一组的组数进行比较
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int a[N];
int t;
int n,k;
int main()
{
	cin>>t;
	while(t--){
		cin>>n;
		int flag;
		for(int i=0;i<n;i++){
		    cin>>a[i];
		    if(i==0) flag=a[i];
		    else flag&=a[i];
		}
		int res=0;
		int t;
		for(int i=0;i<n;i++){
		    t=a[i];
		    while(t!=0&&i<n){
		        t&=a[i];
		        if(!t) break;
		        i++;
		    }
		    res++;
		}
// 		cout<<t<<' '<<flag<<endl;
		if(t>flag) cout<<max(res-1,1)<<endl;
		else cout<<res<<endl;
	}
	return 0;
} 
/*
&不增
*/
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值