Codeforces Round #721 (Div.2)部分题解

前段时间要准备考试,就没有刷codeforces;今天刚考完就来了(但是为什么是520!),不过状态不好+不想认真打就拿了小号打一下,代码质量也很差(甚至连样例都懒得测根据WA来改代码),大家随便看看思路就好。

A. And Then There Were K

题意:给定 n ,找到最大的 k 使得 n\&(n-1)\&\cdots\&k=0 .

题解:这题十分适合拿来做面试题,显然只要找到 n 二进制下的最高位(假设为 t ),然后取 k=2^t-1 即可,这是因为 k>2^t-1 时,最高位始终为1,没法取与成0.

代码:

#include<cstdio>
using namespace std;
	int t,n;
int main()
{
	scanf("%d",&t);
	while (t --)
	{
		scanf("%d",&n);
		int k = 0;
		for (int i = 31;i >= 0;i --)
			if (n & (1 << i))
			{
				k = i;
				break;
			}
		printf("%d\n",(1 << k) - 1);
	}
	return 0;
}

B1. Palindrome Game (easy version)

题意:对一个01回文串,每次有两个操作(1)将一个0变为1,消耗1美元;(2)将字符串翻转,不消耗金钱,但只有在非回文情况下才可做该操作,且上家做完该操作后下家不可做同样的跟进。当串全为1时游戏结束,花钱少的人胜,问给定串后先后手是否存在必胜策略。

题解:先手是很绝望的,因为他初始时不能翻转,但只要他改变了一个数,就破坏了回文串的属性,使得后手可以翻转;因此显然大多数情况下后手能让先手多花两美元,从而后手胜。

但也有一些特殊情况,比如一开始串就全为1,那么直接平局;

假如回文串是奇数长度且最中间为0,那么先手还可以操作一下:如果此时只有这中间的一个0,那自然还是输;但假如不是,那么先手把中间的0转换了就先后手易位,两 极 反 转,此时虽然先手多花了1美元,但根据上面的结论,局势转换后后手方可以让先手多花两美元,因此可必胜。

代码:

#include<cstdio>
using namespace std;
	int t;
	int n;
	char a[1001];
int main()
{
	scanf("%d",&t);
	while (t --)
	{
		int sum = 0;
		scanf("%d",&n);
		for (int i = 1;i <= n;i ++)
		{
			char c = getchar();
			while (c != '0' && c != '1')
				c = getchar();
			a[i] = c;
			if (c == '0')
				++sum;
		}
		if (sum % 2)
		{
			if (sum == 1)
				printf("BOB\n");
			else printf("ALICE\n");
			continue; 
		}
		if (sum == 0)
			printf("DRAW\n");
		else printf("BOB\n");
	}
	return 0;
} 

B2. Palindrome Game (hard version)

题意:在B1的基础上,初始串不一定为回文串;

题解:不为回文串的情况下先手主动权就很多,后手必须尽快使得串变为回文以抢夺翻转权;那么有多少个不对称的数就是一个关键,我们记为 num ;然后,对称的0的数目,我们记为 sum

绝大多数情况下先手必胜,当 n 为偶数或 n 为奇数但最中间的数为1,如果 num=1 ,假如 sum>0 ,先手直接取掉num,转换成B1题目,从而必胜,假如sum=0那么就翻转,后手取完num游戏结束,仍为先手胜利;如果 num\geq 2 ,此时若sum=0则先手维持翻转即可,若sum>0那么先手先翻转若干次,直到后手取剩下一个num的时候把它取走,又转化为B1题目,而且差距扩大到了 num-1+2 美元。

特殊情况是n为偶数且正中间的数为0,此时有且仅有一种和的可能:num=1且sum=1,也就是除了正中间的0以外没有对称的0,此时先后手必须各取一个0。

代码:

#include<cstdio>
using namespace std;
	int t;
	int n;
	char a[1001];
int main()
{
	scanf("%d",&t);
	while (t --)
	{
		int sum = 0;
		scanf("%d",&n);
		for (int i = 1;i <= n;i ++)
		{
			char c = getchar();
			while (c != '0' && c != '1')
				c = getchar();
			a[i] = c;
		}
		int num = 0;
		bool isPalindrome = true;
		for (int i = 1;i <= n / 2;i ++)
		{
			if (a[i] != a[n - i + 1])
			{
				isPalindrome = false;
				++num;
			}
			if (a[i] == '0' && a[i] == a[n - i + 1])
				sum += 2;
		}
		if (n % 2 && a[n / 2 + 1] == '0')
			++sum;
		if (isPalindrome)
		{
			if (sum % 2)
			{
				if (sum == 1)
					printf("BOB\n");
				else printf("ALICE\n");
				continue; 
			}
			if (sum == 0)
				printf("DRAW\n");
			else printf("BOB\n");
		}
		else
		{
			if (n % 2 && a[n / 2 + 1] == '0')
			{
				if (num == 1 && sum == 1)
					printf("DRAW\n");
				else printf("ALICE\n");
			}
			else
			{
				printf("ALICE\n");
			}
		}
	}
	return 0;
}

C. Sequence Pair Weight

题意:求:

\sum_{1\leq i< j\leq n}\sum_{i\leq l<r\leq j}[a_l==a_r]

题解:这一看就是个组合计数,而且需要一点点递推的思想,还是很巧妙的。首先每对相同的数对答案的贡献为 l(n-r+1) ,因为包含他们的子区间,从左可以延伸 l 个数,从右可以延伸 n-r+1 个数。那么我们先将数据离散化,然后维护每一个数值 l 的和,每次扫到这个数值时,它的贡献就为这个和与 n-i+1 的乘积(他的现实含义是以该数为较大index的pair的贡献),然后再把和加上 i ,时间复杂度为 O(n\log n+n)=O(n\log n) ,主要包含离散化的代价.

代码:

#include<cstdio>
#include<algorithm>
using namespace std;
	struct newdata
	{
		int v,label;
	};
	int t;
	int n;
	int a[100001];
	int num[100001];
	int last[100001];
	int head[100001];
	long long length[100001];
	newdata b[100001];
bool cmp(newdata i,newdata j)
{
	return i.v < j.v;
}
int main()
{
	scanf("%d",&t);
	while (t --)
	{
		scanf("%d",&n);
		for (int i = 1;i <= n;i ++)
		{
			scanf("%d",&a[i]);
			b[i].v = a[i];
			b[i].label = i;
		}
		sort(b + 1,b + n + 1,cmp);
		a[b[1].label] = 1;
		for (int i = 2;i <= n;i ++)
			if (b[i].v == b[i - 1].v)
				a[b[i].label] = a[b[i - 1].label];
			else a[b[i].label] = a[b[i - 1].label] + 1;
		for (int i = 1;i <= n;i ++)
		{
			num[i] = 0;
			head[i] = 0;
			length[i] = 0;
		}
		for (int i = 1;i <= n;i ++)
		{
			last[i] = head[a[i]];
			head[a[i]] = i;
		}
		long long ans = 0;
		for (int i = 1;i <= n;i ++)
		{
			if (last[i])
			{
				ans += (long long)(n - i + 1) * length[a[i]];
			}
			length[a[i]] += i;
		}
		printf("%lld\n",ans);
	}
	return 0;
}

发布于 2021-05-21 00:50