codeforces刷题

本文探讨了两道编程竞赛题目,第一题涉及在限制条件下邀请人数的最大化问题,采用二分查找策略求解。第二题是求序列异或值的和,通过分析二进制位的贡献解决。第三题研究序列变化优化问题,使用01背包方法找到最大可能值。这些题目展示了在算法设计和复杂计算中的策略和技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

https://codeforces.com/contest/1610/problem/C
题意:有n个人,富裕程度1~n, 要举行派对,要求对于每个人,来的人中比他富的不超过 a i a_i ai, 比他穷的不超过 b i b_i bi,问最多能邀请多少人来.

思路:二分答案,把答案最大化。关键是怎么统计能邀请多少人,假设可以邀请mid人,对于每个人,记录当前已经邀请了cnt人,如果 b i b_i bi>=cnt,并且 a i a_i ai>=mid-cnt-1, 那么说明可以邀请这个人。
code :

#include<bits/stdc++.h>

using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

//head
const int N=2e5+10;
int a[N],b[N];
int n;

bool check(int mid)//mid为答案选几个
{
	int cnt=0;//当前选了cnt个
	for(int i=1;i<=n;i++){
		if(b[i]>=cnt&&a[i]>=mid-cnt-1) cnt++;
		
	}
	if(cnt>=mid) return 1;
	return 0;
}
void solve()
{
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i]>>b[i];
	}
	int l=1,r=n;
	while(l<r)
	{
		int mid=l+r+1>>1;
		if(check(mid)) l=mid;
		else r=mid-1;
	}
	cout<<r<<endl;
}
signed main()
{
	ios;
	int t;
	cin>>t;
	//t=1;
	while(t--)
	{
		solve();
	}
	return 0;
}

添加链接描述

题目大意:给定一个序列 a ,a为未知序列,长度为n,给m个已知信息,为从 a l a_l al a r a_r ar 的或的值为x, a的所有子集中元素异或值的和。

思路:对二进制下每一位分析,就比如对于每个元素的第3位,构成一个集合b,b={b1,b2,b3,…bn}, 对于某个 b i b_i bi, 若 b i b_i bi 为1,则将集合分为含有 b i b_i bi和不含 b i b_i bi的两种集合,其数量均为 2 n − 1 2^{n-1} 2n1个,其异或值为1的一定在这两个集合的其中一个,因此每一位1的贡献为 :当前1对应的值 * 2 n − 1 2^{n-1} 2n1, 那么怎么判断当前位是否有1呢?直接将所有x或起来即可。

code:

#include<bits/stdc++.h>

using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
//head
typedef pair<int,int> pii;
const int N=2e5+10,mod=1e9+7;

int qmi(int a,int k)
{
	int ans=1;
	while(k)
	{
		if(k&1) ans=(ans*a)%mod;
		a=(a*a)%mod;
		k>>=1;
	}
	return ans;
}
void solve()
{
	int n,m;
	cin>>n>>m;
	int ans=0;
	while(m--)
	{
		int l,r,x;
		cin>>l>>r>>x;
		ans|=x;
	}
	cout<<ans*qmi(2,n-1)%mod<<endl;
}
signed main()
{
	ios;
	int t;
	cin>>t;
	while(t--)
	{
		solve();
	}
	return 0;
}

添加链接描述

题目大意 : 将长度为n的序列a,(初始全为1),最多经过k次变化,能获得的最大值是多少。每次变化操作为: a i a_i ai+= a i a_i ai/x;将 a i a_i ai 变为 b i b_i bi 可以获得 c i c_i ci 的值。

思路:01背包求解,预处理出来每个 a i a_i ai 变为 b i b_i bi 的最小次数, 以该次数为体积,以 c i c_i ci 为价值做一遍01背包即可。需要注意的是每个数最大变化次数不超过12次,而k值较大,因此在求体积时要与k取 min。

code:


#include<bits/stdc++.h>

using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

//head
const int N=1e5+10;
int b[N],w[N],v[N];
int n,k;
int f[N],cnt[100010];

void solve()
{
	memset(f,0,sizeof f);
	cin>>n>>k;
	int sum=0;
	for(int i=1;i<=n;i++) cin>>b[i];
	for(int i=1;i<=n;i++) cin>>w[i];
	for(int i=1;i<=n;i++) {
		v[i]=cnt[b[i]];
		sum+=v[i];
	}
	k=min(k,sum);
	for(int i=1;i<=n;i++){
		for(int j=k;j>=v[i];j--){
			f[j]=max(f[j-v[i]]+w[i],f[j]);
		}
	}
	cout<<f[k]<<endl;
}
signed main()
{

	memset(cnt,0x3f,sizeof cnt);
	cnt[1]=0;
	for(int i=1;i<=1e3;i++){
		for(int j=1;j<=i;j++){
			cnt[i+(i/j)]=min(cnt[i+(i/j)],cnt[i]+1);
		}
	}

	//ios;
	int t;
	cin>>t;
	//t=1;
	
	while(t--)
	{
		solve();
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值