Codeforces Round #626 (Div2)A&B&C

Codeforces Round #626 (Div. 2, based on Moscow Open Olympiad in Informatics)

A&B&C

A

题目链接

A题链接

题目大意

签到题,就是给一串数字,找到他的一个合为偶数的子集。

解析

先找有没有偶数,如果有偶数就输出1和偶数的位置;如果没有偶数,找到两个奇数,输出2和两个奇数的位置即可。

代码

#include
#define ll long long
using namespace std;
const int MAXN = 200;
int A[MAXN];
int n;
int main()
{
	int T;
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d", &n);
		int even = 0;
		int odd1 = 0;
		int odd2 = 0;
		for (int i = 1; i <= n; i++)
		{
			scanf("%d", &A[i]);	
		}	
		for (int i = 1; i <= n; i++)
		{
			if (A[i]%2)
			{
				if (odd1 == 0)
				{
					odd1 = i;
				}
				else
				{
					odd2 = i;
				}
			}
			else
			{
				even = i;
			}
		}
		if (even)
		{
			printf("1\n%d\n", even);
		}
		else if (odd1 && odd2)
		{
			printf("2\n%d %d\n", odd1, odd2);
		}
		else
			printf("-1\n");
	 } 
	return 0;
}

B

题目链接

B题链接

题目大意

给定三个数,n,m,k;
以及两个01数组,分别是长度为n的a[]和长度为m的b[]。
将a向量与b向量相乘,获得一个矩阵。
问矩阵中,单元数(元素个数)为k的的子矩阵有多少个。

解析

这个挺难搞,真还挺难搞(我的水平相当一般)。

首先我们这样考虑。
将a数组作为向量的列,b数组乘上去就是这一列存不存在,1的时候下一列就还是a向量,0的时候下一列就是全0。
那么我们现实单考虑一列的时候。
假设,k为2,这向量a的这一列是
01110001111’(横的写不占地方)
那么对于这一列,在第二个位置,有一个,第三个位置有一个。
三个1连起来可以有2个,四个一连起来可以有3个。

那么对于每一次这样的访问,我要怎么以O(1)的复杂度,获得这一列到底有多少个长度为k的子集呢?(对于宽度为1的竖棍棍情况)

我们扫一遍A数组,对于连续的1进行统计,统计有多少个连续的1。当遇到0的时候,我们就把这个连续的个数对应的hash值+1,hash数组第一个目标是记录的是长度为X的连续为1的数组有多少个。

(回头想我搞的麻烦了,如果直接连续的算,就不用两次后缀和了,应该,先这样搞吧)。

然后我们需要做两次后缀和(对这个hash的数组),第一次对应的是,如果后面很多个连续的存在(比如说4),那么前面的也存在(比如说1,2,3)。(这时候hash数组记录长度为X的连续为1的数组有多少个)
然后第二次后缀和,是为了O(1)的获取,某个长度对应的情况数量。
(hash数组此时记录长度大于等于X的连续1有多少个)
总之算出来的结果是,对于
111 这个数组
hash数组应该是 3 2 1
1111 就是
4 3 2 1

接着我们考虑,横向的对于B向量又该怎么搞。

对于一个k值,比如说6,我们有可能有很多种行的排列方式,比如说1行,2行,3行和6行。
那么我们如何判断行的数量。
就是扫一遍b数组,记录当前扫过的连续的1的数量为now,如果k能够整除now,那么对应的行数就是k/now。

然后的问题是如何统计数量。
假设我们现在b中连续1的长度比较长,此时k为6,横向一行的长度已经能够填充排成1行,2行,3行和6行的全部情况,那么累加一列上去,对应的以k/now(now=1,2,3,6)为下标的hash数组的值,都需要累加到答案上。(都可以向右再移动一列)

那么我们可以额外用一个变量times,记录每一段b中连续的1,他累加了多少情况在上面(对应排成1行,2行,3行等的总数)。遇到b中为0的元素就清零,然后每次先判断能不能整除now,先加times,再把times累加到答案上。就搞完了。

注意一下k/now不要访问越界了。

代码

#include
#define ll long long
using namespace std;
const int MAXN = 500000;
ll k;
int n, m;
int A[MAXN], B[MAXN];
ll mp[MAXN]; 
int main()
{
	scanf("%d%d%lld", &n, &m, &k);
	for (int i = 1; i <= n; i++)
	{
		scanf("%d", &A[i]);
	}
	for (int i = 1; i <= m; i++)
	{
		scanf("%d", &B[i]);
	}	
	int nums = 0;
	for (int i = 1; i <= n+1; i++)
	{
		if (A[i])
		{
			nums++;
		}
		else
		{
			mp[nums]++;
			nums = 0;
		}
	}
	for (int i = MAXN-2; i >= 0; i--)
	{
		mp[i] = mp[i+1]+mp[i];
	}
	for (int i = MAXN-2; i >= 0; i--)
	{
		mp[i] = mp[i+1]+mp[i];
	}
	ll ans = 0;
	int now = 0;
	ll times = 0;
	for (int i = 1; i <= m; i++)
	{
		if (B[i])
		{
			now++;
			if (k%now == 0 && k/now < MAXN)
			{
				times += mp[k/now];
			}
			ans += times;
		}
		else
		{
			now = 0;
			times = 0;
		}
	}
	printf("%lld", ans);
	return 0;
}

C

题目链接

C题链接

题目大意

给一串括号序列,每次移动任意调整子串中括号的顺序最少,每次移动花费的代价是子串的长度,问最少代价是多少。

解析

这个难度我觉得比B题低。
肯定是贪心的先满足顺序不对的括号,所以说只要做两个栈,st1和st2。
从左往右扫。
st1里面存储的是’)'的下标(没有与之对应的右括号)。
st2里面存储的是‘(’,就是可以抵消‘)’的(现在想记个数字就行)。

遇到了‘(’:
当st2里面没东西的时候,就压入到st1中。
如果st2里面有东西,就pop出来,消掉一个。

遇到了‘)’:
如果st1里面有东西,就消掉一个。
如果st1在这次操作中,消空了,那么就说明中间的这一段,都得重新排序,做一次操作,代价是i(循环变量)-最后pop出来的值+1。累积到答案中。
如果st1消除之前就是空的,那么就加到st2中去。

然后就没了。

代码

#include
#define ll long long
using namespace std;
const int MAXN = 2000000;
char s[MAXN];
int n;
int toto, totc;
stack <int> st1;
stack <int> st2;
int main()
{
	scanf("%d", &n);
	scanf("%s", s+1);
	for (int i = 1; i <= n; i++)
	{
		if (s[i] == '(')
		{
			toto++;
		}
		else if (s[i] == ')')
		{
			totc++;
		}
	}
	if (toto != totc)
	{
		printf("-1");
	}
	else
	{
		ll ans = 0;
		for (int i = 1; i <= n; i++)
		{
			if (st2.empty() && s[i] == ')')
			{
				st1.push(i);
			}
			else if (st2.size() && s[i] == ')')
			{
				st2.pop();
			}
			else if (st1.size() && s[i] == '(')
			{
				int tmp = st1.top();
				st1.pop();
				if (st1.empty())
					ans += i-tmp+1;
			}
			else if (st1.empty() && s[i] == '(')
			{
				st2.push(i);
			}
		}
		printf("%lld", ans);
	}
	return 0;
}

emmmmm,就做出来这么三个,后面的如果补了继续更新。

你可能感兴趣的:(思维题,题解)