Codeforces Round #832 (Div. 2)

A. Two Groups

题目链接:Problem - A - Codeforces

样例输入: 

4
2
10 -10
4
-2 -1 11 0
3
2 3 2
5
-9 2 0 0 -4

样例输出:

0
8
7
11

题意:给定n个数,我们可以将这n个数划分至2个集合,允许其中一个集合为空集,问第一个集合内的数的和的绝对值-第二个集合内的数的和的绝对值的最大值是多少

分析:直接将所有的数放至一个集合求和的绝对值即可

首先我们知道如果两个集合的和都是正数或者都是负数,那么肯定是合并至一个集合更优

下面就来分析一个集合元素和是正数另一个集合元素和是负数的情况:

如果第二个集合的元素和绝对值小于第一个集合的元素和绝对值,那么将两个集合合并成一个集合后元素和绝对值就等于两个集合的元素和绝对值之差,是没有什么影响的,而如果第二个集合的元素和绝对值大于等于第一个集合的元素和绝对值,那么如果不合并集合,最后的结果是一个负数,那么显然是不如合并成一个集合更优的,所以将所有数放至在一个集合的情况是最优的。

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int n;
		scanf("%d",&n);
		long long ans=0;
		int t;
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&t);
			ans+=t;
		}
		printf("%lld\n",abs(ans));
	}
	return 0;
}

B. BAN BAN

题目链接:Problem - B - Codeforces

样例输入:

2
1
2

样例输出:

1
1 2
1
2 6

题意:给定一个n,代表连续n个“BAN”连接而成的字符串,我们可以对这个字符串进行操作,每次操作选择两个字符并交换其位置,问至少需要交换多少次使得原字符串中不含有子序列"BAN"。

分析:我们只要使得所有的字符B后面的字符A全部交换至字符B的前面即可,这样的交换次数就是n/2的上取整。交换策略也很简单,就是每次使得最靠前的一个字符B和最靠后的一个字符A交换位置即可,对于n为奇数的情况,最后使得位于中间的B和最后一个字符交换位置即可。

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int n;
		scanf("%d",&n);
		printf("%d\n",(n+1)/2);
		for(int i=1;i<=n/2;i++)
			printf("%d %d\n",(i-1)*3+1,(n-i)*3+2);
		if(n&1) printf("%d %d\n",n/2*3+1,3*n);
	}
	return 0;
}

C. Swap Game

题目链接:Problem - C - Codeforces

样例输入: 

3
2
1 1
2
2 1
3
5 4 4

样例输出:

Bob
Alice
Alice

题意:给定一个长度为n的数组a,Alice和Bob轮流操作,每次操作都是将a[1]减1后选择一个i大于1小于等于n,然后让a[1]与a[i]交换位置,a[1]操作前为0,则不能进行减一操作,不能操作的一方输。问在两人都足够聪明的前提下最后谁会赢得比赛。

分析:通过简单模拟我们可以发现,先手可以选择一个i,保证每次操作后都把a[i]交换至第一个位置让后手对这个数进行减1操作,但是这个i不能是第一个数,同理后手也可以选择一个数让先手一直操作这个数,这个数不能是先手所选择的那个数,按照这个策略,显然对于任意一方都希望让另一方操作一个尽可能小的数,所以现在问题就转化为判断第一个数和其余的数中的最小值的大小关系,如果第一个数小于等于其余数的最小值,那么当先手操作完后,后手肯定会再次选择把第一个数换回第一个位置,那么这样先手就会一直操作第一个数,直至不能操作为止,这样先手必败。如果第一个数大于其余数的最小值,那么先手就会把最小值换至第一个为止,这样后手就会一直操作最小值,这样后手必败。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N];
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int n;
		scanf("%d",&n);
		int mn=0x3f3f3f3f;
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			if(i!=1) mn=min(mn,a[i]);
		}
		if(a[1]<=mn) puts("Bob");
		else puts("Alice");
	}
	return 0;
}

D. Yet Another Problem

题目链接:Problem - D - Codeforces

样例输入:

7 6
3 0 3 3 1 2 3
3 4
4 6
3 7
5 6
1 6
2 2

样例输出:

-1
1
1
-1
2
0

题意:给定一个长度为n的数组,然后给定一个q代表询问次数,每次询问给出一个区间[l,r],我们可以对这个区间内的一个奇数长度的子区间进行操作,操作后的结果就是这个子区间内的所有数都变为这个子区间内的所有数的异或和,输出能够将区间[l,r]内的元素全变为0的最少操作次数,如果不能就输出-1.

分析:首先一个重要的性质就是我们对一个区间进行操作后并不会改变该区间的异或和,所以对于一开始给定区间内元素异或和不为0的直接输出-1即可。

如果给定区间内所有元素均为0那么我们就直接输出0即可。

否则我们分区间长度的奇偶性进行讨论:

如果区间长度是奇数,那么我们直接选择整个区间进行一次操作就可以使得整个区间全变为0,所以对于这种情况我们直接输出1即可。

如果区间长度是偶数:

我们先看一下区间的两端元素是否存在0,如果存在0的话,我们选择除了这个0以外的其他所有数进行一次操作即可,这样结果就是1.

否则我们需要判断是否有后缀奇数长度的区间使得异或和为0(前缀也是等价的,因为偶数减去一个奇数得到一个奇数,那么后面奇数长度的异或和等于0,由于整个区间异或和等于0,说明前面的奇数长度的区间异或值也是0),那么我们可以先对这个区间进行一次操作,使得产生若干个后缀0,那么我们再对未选区间+一个0进行一次操作,这样就能够通过两次操作使得整个区间内的数全变为0了。

判断是否存在后缀奇数长度的区间使得异或和为0时不能暴力判断,我们可以记录一下以当前位置作为区间右边界可以满足异或值为0的最大区间左边界的位置,保证区间长度为奇数,那么我们就直接用两个map分奇数和偶数存一下即可。

最后直接判断以r为右边界的最大左边界与l的关系即可

细节见代码:

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int a[N],l[N];
long long s[N],x[N];
int main()
{
	int n,m;
	cin>>n>>m;
	map<int,int>mp[2];
	mp[0][0]=0;
	int t=0;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		s[i]=s[i-1]+a[i];
		x[i]=x[i-1]^a[i];
		t^=a[i];
		int s=(i&1)^1;
		if(mp[s].count(t))
			l[i]=mp[s][t]+1;
		mp[s^1][t]=i;
	}
//	for(int i=1;i<=n;i++)
//		printf("%d %d\n",i,l[i]);
	while(m--)
	{
		int ll,rr;
		scanf("%d%d",&ll,&rr);
		if(x[rr]^x[ll-1]) puts("-1");
		else
		{
			if((rr-ll+1)&1)
			{
				if(s[rr]-s[ll-1]==0) puts("0");
				else	puts("1");
			}
			else
			{
				if(s[rr]-s[ll-1]==0) puts("0");
				else if(a[ll]==0||a[rr]==0) puts("1");
				else
				{
					if(l[rr]>=ll) puts("2");
					else puts("-1");
				}
			}
		}
	}
	return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值