Codeforces Round 912 (Div. 2) A—D1

A. Halloumi Boxes

题目链接

题意

给定一个包含n个元素的数组,每次可以最多可以反转数组中的k个元素,操作次数无上限,询问是否可以将数组变成非递增排列

分析

对于操作次数无上限的问题,我们往往采用贪心的策略,可以发现只要每次反转两个元素,就一定可以将数组变为非递增的,因此只要操作次数大于等于2,就一定可以,对于操作次数<2的,除非原数组就是非递增数组,否则一定不可以

代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
//#include<queue>
//#include<map>
//#include<vector>
//#include<set>
//#include<deque>
//#include<bitset>
//#include<functional>

#define ll long long
#define inf 0x3f3f3f3f
using namespace std;

typedef pair<int,int>PII;

const int N=110;
int f[N];	
	
int main()
{
	int t;cin>>t;
	while(t--)
	{
		int n,k;
		cin>>n>>k;
		for(int i=1;i<=n;i++) cin>>f[i];
	    if(is_sorted(f+1,f+1+n) || k>=2) puts("YES");
		else puts("NO");
	}
	
	
	return 0;
}

B. StORage room

题目链接

题意

给定一个n行n列的矩阵m,除了主对角线全部为0之外,其余的m[i][j]都等于a[i] | a[j],请你判断是否有合法的a数组,如果有输出yes与其元素,如果没有则输出no

分析

第一次做到位运算的题真是无从下手,是一道不错的构造题

或运算的性质,只要两个数有一个是1结果就是1

首先根据第二组样例

 
0335
3037
3307
5770

第一行从第二列开始所代表的意义分别为

a[1] | a[2]=3    a[1] | a[3]=3    a[1] | a[4]=5

其中3=11,5=101

由于或运算的性质,只要两个数其中有一位该位是1,那么结果该位也一定是1,为了使答案最优(更容易满足其他元素),所以我们可以确定a[1]的第1位和第3位为1,那我们只需要找到a[1]上哪几位是0即可,对于该行所有元素如果相同位都为1,那么a[1]该位也为1,有一位为0,那么a[1]该位也为0。所以结论就是a[i]=矩阵m的第i行元素相与(除了对角线)

代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
//#include<queue>
//#include<map>
//#include<vector>
//#include<set>
//#include<deque>
//#include<bitset>
//#include<functional>

#define ll long long
#define inf 0x3f3f3f3f
using namespace std;

typedef pair<int,int>PII;
const int N=1e3+10;
int f[N][N];
int ans[N];	
	
int main()
{
	int t;cin>>t;
	while(t--)
	{
		int n;cin>>n;
		for(int i=1;i<=n;i++) ans[i]=-1;//初始化为-1
		
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++) 
			{
				cin>>f[i][j];
			    if(j==i) continue;
				if(ans[i]==-1) ans[i]=f[i][j];
				else ans[i]=ans[i] & f[i][j];
			}
		}
		int flag=1;
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				if(i==j) continue;
				if((ans[i] | ans[j])!=f[i][j])
				{
					flag=0;
					break;
				}
			}
		}
		if(flag) 
		{
			puts("YES");//答案最小为0
			for(int i=1;i<=n;i++) cout<<max(ans[i],0)<<" ";
			cout<<endl;
		}
		else puts("NO");
	}
	
	
	return 0;
}

C. Theofanis' Nightmare

题目链接

题意

给定一个数组,请你将数组划分为若干个区间,定义每个区间的价值为每个区间内元素之和乘该区间的下标,求最大的总价值是多少

分析

假设将原数组划分为了4个区间,用mi表示第i个区间的元素之和,那么总价值为:m1*1+m2*2+m3*3+m4*4,

可等价于

m1+m2+m3+m4  +m2+m3+m4  +m3+m4  +m4

再分析一下划分区间的代价

对于a b,如果我们不划分区间,那么总价值为 a+b

如果我们将ab划分为两个区间,那么总价值为 a+b*2,即a+b+b

可以发现,如果我们选择在第i个元素处划分区间,那么产生的总价值会在原先的总价值上加一个i元素之和的所有元素之和,显然当第i个元素之后的元素大于0时,划分区间能得到更多的价值,所以我们用后缀和记录每个元素之后的元素之和,如果其后缀和大于0,那么我们在这里划分区间即可

代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
//#include<queue>
//#include<map>
#include<vector>
//#include<set>
//#include<deque>
//#include<bitset>
//#include<functional>

#define ll long long
#define inf 0x3f3f3f3f
using namespace std;

typedef pair<int,int>PII;


	
	
int main()
{
	int t;cin>>t;
	while(t--)
	{
		int n;cin>>n;
		vector<ll>a(n+1);
		vector<ll>b(n+2);
		b[n+1]=0;//防止越界
		for(int i=1;i<=n;i++) cin>>a[i];
		for(int i=n;i>=1;i--) b[i]=b[i+1]+a[i];//后缀和
		
		ll ans=0;
		for(int i=1;i<=n;i++)
		{
			ans+=a[i];
			if(b[i+1]>0) ans+=b[i+1];
		}
		cout<<ans<<endl;
	}
	
	
	return 0;
}

D1. Maximum And Queries (easy version)

题目链接

题意

给定一个大小为n的数组,和m次询问,每次询问给定一个整数k,代表最多可操作k次,每次操作可以将数组中的一个数加1,求最多进行k次操作后,数组中所有元素相与的最大值

分析

对于相与得到的结果,只有每一位都是1时,才能产生贡献,为求相与的最大值,我们可以从高位枚举,求出将所有元素的该位变为1需要的操作次数之和是否小于k,想法不是很难,实现起来很麻烦,需要对位运算十分熟练

代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
//#include<queue>
//#include<map>
//#include<vector>
//#include<set>
//#include<deque>
//#include<bitset>
//#include<functional>

#define ll long long
#define inf 0x3f3f3f3f
using namespace std;

typedef pair<int,int>PII;
const int N=1e5+10;
//ll a[N];
//ll b[N];
	
	
int main()
{
	ll n,m;
	cin>>n>>m;
	vector<ll> a(n+1);
	vector<ll>b(n+1);
	for(int i=1;i<=n;i++) cin>>b[i];
	while(m--)
	{
		for(int i=1;i<=n;i++) a[i]=b[i];
		ll op;cin>>op;
		ll res=0;
		for(int k=60;k>=0;k--)//从高位开始判断
		{
			ll ans=0;//将a的所有元素的第k位都变成1的操作次数
			for(int i=1;i<=n;i++)
			{
				if((a[i]>>k&1)==0) //a[i]的第k位为0
				{
					ans=ans+(1ll<<k)-a[i]%(1ll<<k);//将这一位变成1所需要的操作次数
					if(ans>op) break;//操作次数大于op直接退出
				}
			}
			
			if(ans<=op)//操作次数小于op
			{
				op-=ans;//剩下的操作次数
				res+=1ll<<k;//将当前这一位都变成1之后产生的贡献
				for(int i=1;i<=n;i++)
				{
					if((a[i]>>k&1) == 0) a[i]=0;//将这个元素第k位变为1之后,第k位之后都是0,所以直接变成0也不影响后续位的操作
				}
			}
			
		}
		cout<<res<<endl;
	}
	
	
	return 0;
}

寒假vp第一场

  • 18
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值