Bootstrap

截断数组 && LR-remainders 24.2.20刷题(摸鱼)

AcWing 每日一题:截断数组

给定一个长度为 n的数组a[n]

现在,要将该数组从中间截断,得到三个非空子数组。

要求,三个子数组内各元素之和都相等。

请问,共有多少种不同的截断方法?

1≤n≤1e5,−10000≤a≤10000

        数组元素顺序固定,问题转化为:是否可以将数组分成三个区间和相等的部分;

#include<iostream>
#include<cmath>
#include<vector>
#include<algorithm>
#include<cstring>
typedef long long ll;
const int M = 100005;

using namespace std;

int main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int n,f = 0;
	ll k = 0, sum = 0, cnt = 0;
	cin >> n;
	vector<int>a(n);
	for (int i = 0; i < n; i++)
	{
		cin >> a[i];
		k += a[i];         //计算总和
	}
	if (k % 3 || n<3)
	{
		cout << 0 << '\n';
		return 0;
	}
	ll ave = k / 3;        //每个区间的和
	for (int i = 0; i < n-1; i++)
	{
		sum += a[i];
		if (sum == ave * 2)//判断第二个区间,防止相同元素的重复计算,if顺序不能颠倒
			cnt += f;
		if (sum == ave)    //判断第一个区间
			f++;
	}
	cout << cnt << '\n';
	return 0;
}

Cf Round 927 (Div.3)        C:LR-remainders

You are given an array a of length n, a positive integer m, and a string of commands of length n. Each command is either the character 'L' or the character 'R'.

Process all n commands in the order they are written in the string s. Processing a command is done as follows:

  • First, output the remainder of the product of all elements of the array a when divided by m.
  • Then, if the command is 'L', remove the leftmost element from the array a, if the command is 'R', remove the rightmost element from the array a.

Note that after each move, the length of the array a decreases by 11, and after processing all commands, it will be empty.

Write a program that will process all commands in the order they are written in the string s (from left to right).

1≤n≤2⋅10e5,1≤m≤10e4

给你一个长度为 n 的数组 a 、一个正整数 m 和一串长度为 n 的命令。每条命令要么是字符 "L",要么是字符 "R"。

按照字符串 s 中的顺序处理所有 n 命令。处理一条命令的步骤如下:

  • 首先,输出数组 a 中所有元素除以 m 后的乘积余数。
  • 然后,如果命令是 "L",则从数组 a 中删除最左边的元素;如果命令是 "R",则从数组 a 中删除最右边的元素。

注意,每次移动后,数组 a 的长度都会减少 1 ,处理完所有命令后,数组 a 将为空。

请编写一个程序,按照字符串 s 中的顺序(从左到右)处理所有命令。

        比赛的时候WA了,今天官方题解也没出来。。

        题意很简单:输入一个数组,再输入一个只有‘L’和‘R’的字符串,L表示删除数组最左边的元素,R表示删除数组最右边的元素。每次操作输出数组中所有数的乘积mod m;

        比赛的时候从正面写的代码,按照字符串的顺序一个一个删除再取模。TLE先不说,就WA提前宣告死亡。对我来说最大的问题是L和R的位置判断,把 l 定为0还是1,r 定为n还是n+1。

        实际上这一题要逆向考虑,原题说的是删除,我们考虑添加元素。

        对于字符串的L和R,原本的删除改为添加,那么每一个字符就对应了一次乘法操作和一次取模操作。因此,首先将字符串倒序,从最后一个字符开始遍历。当遍历到‘L’,用基础值k乘上第L个数字的左边一个数据(原先被删掉的数据,现在要补回来)。当遍历到‘R’同理。但是就会有一个漏洞:最后一个删除掉的数字并没有被乘上,即第一个应该添加的数字并没有被取到。

        那么现在发现一个问题:怎么判断第一个添加到数组的数字?

        如果数组大小是奇数,那么L出现的次数就是从左往右数最后删除的数字的位置(R同理)。

如果数组大小是偶数,那么根据L和R的顺序不同,最后一个数字的结果会有所不同。(四个数字,LLRR和LRRL最后删除的数字不同)但都是从左往右的L出现的次数个或者+1个(R同理);

        这种情况要怎么办?

        从CF榜第一的代码里学到的方法:在进行L和R的遍历判断时,如果以L作为基准,若遍历到‘L’,乘上L的左边一位。若遍历到‘R’,乘上R的这一位,然后将R往右移动一位。

        这样做不仅可以准确乘上第一个应该添加的数字,并且对于偶数大小的数组正好解决的所谓的“+1”问题。orz

以下是AC代码:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
typedef long long ll;
const int M = 200005;
 
using namespace std;
 
void solve() {
	int n, m;
	string s;
	cin >> n >> m;
	vector<int>a(n);
	vector<int>ans(n);
	for (int i = 0; i < n; i++)
		cin >> a[i];
	cin >> s;
	int len = s.length();
	int cnt = 0;
	for (int i = 0; i < len; i++)
		if (s[i] == 'L')cnt++;
	int l = cnt;
	int r = l;
	int k = 1;
	for (int i = n - 1; i >= 0; i--) {
		if (s[i] == 'L')
			k = k * a[--l] % m;
		else
			k = k * a[r++] % m;
		ans[i] = k;
	}
	for (int i = 0; i < n; i++)
		cout << ans[i]<<' ';
	cout << '\n';
}
 
int main() {
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	int t;
	cin >> t;
	while (t--) {
		solve();
	}
	return 0;
}

;