AtCoder - ABC 162 - D(思维)

D - RGB Triplets

题目:

一长为 n 的字符串由 RBG 组成,找出满足以下条件的三元组 (i,j,k) 的数量:

1. i < j < k

2. s[i] != s[j] && s[i] != s[k] && s[j] != s[k]

3. j−i != k−j

数据范围:

1 ≤ N ≤ 4000

思路1:

先算出满足条件1,2的个数,再减去其中不满足条件3的个数。

满足条件1,2的个数即任意选三个不同的字符的个数;再减去满足 j - i = k - j 的个数。
预处理出3种字符的个数a,b,c,总数即为a*b*c;再枚举前两个端点的位置 i,j,判断第三个点的位置 k = 2 * j - i 是否与前两个都不同且满足 j  -i != k - j。总数减去不满足的个数。

Code:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;

#define x first
#define y second
#define int long long

const int N = 200010, INF = 0x3f3f3f3f;

typedef pair<int, int>PII;

int n;
string s;
int a, b, c;

void solve()
{
	cin >> n >> s;
	for (int i = 0; i < n; i++)           //记录3种字符的个数
	{
		if (s[i] == 'R')a++;
		if (s[i] == 'G')b++;
		if (s[i] == 'B')c++;
	}
	
	int res = a * b * c;                  //满足条件1的总数

	//枚举所选的前两个字符
	for (int i = 0; i < n; i++)
		for (int j = i + 1; j < n; j++)
		{
			if (s[i] == s[j])continue;
			if (2 * j - i < n && s[i] != s[2 * j - i] && s[j] != s[2 * j - i])    //第三个字符的位置即中间字符的位置*2-第一个字符的位置:j*2-i
				res--;                      //将不满足条件2的个数减去
		}
			
	cout << res << endl;
}

signed main()
{
	int t = 1;
	//cin >> t;
	while (t--)
	{
		solve();
	}

	return 0;
}

思路2:

将3种字符的位置单独存一块,边枚举边判断。

将 3 种字符的位置单独存(vector[a][]存的是a类字符占据的位置),按取排列中 3 个字符时的先后顺序共枚举9中情况。先后顺序一定时,再顺次枚举每个字符的位置:
第一个字符的位置全枚举;第二个字符的可取位置通过二分查找,从第一个满足位置 j > i 的下标开始枚举;第三个字符的可取位置也用二分查找,从第一个满足位置 k > j 的下标开始枚举。
确定 i, j, k 后再判断是否满足j - i != k - j。如果满足,才说明此时的选法可行。

Code:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;

#define x first
#define y second
#define int long long

const int N = 200010, INF = 0x3f3f3f3f, mod = 1e9 + 7;

typedef pair<int, int>PII;

int n;
string s;
vector<int>v[3];

//在b类字符占据的位置中找第一个大于v[a][i]的位置,返回在b数组中对应的下标,即在v[b]中找第一个大于v[a][i]的值对应的下标
int idx(int b, int a, int i)
{
	return upper_bound(v[b].begin(), v[b].end(), v[a][i]) - v[b].begin();
}

//记录按a,b,c的先后顺序的可行的排列数
int cal(int a, int b, int c)
{
	int ans = 0;
	//枚举3种字符的位置
	for(int i = 0;i < v[a].size(); i++)                    //枚举第一个字符的位置在数组v[a]中的下标
		for(int j = idx(b,a,i); j < v[b].size(); j++)      //枚举位置在i之后的第二个字符的位置在数组v[b]中的下标
			for (int k = idx(c,b,j); k < v[c].size(); k++) //枚举位置在j之后的第三个字符的位置在数组v[c]中的下标
			{
				if (v[b][j] - v[a][i] != v[c][k] - v[b][j]) //这里的v[a][i]表示a类字符存位置的数组中下标为i时对应的s中的位置。等价于判断 j-i != k-j是否成立
					ans++;
			}
	return ans;
}

void solve()
{
	cin >> n >> s;
	for (int i = 0; i < n; i++)                //记录不同字符的位置
	{
		if (s[i] == 'R')v[0].push_back(i);
		if (s[i] == 'G')v[1].push_back(i);
		if (s[i] == 'B')v[2].push_back(i);
	}

	int res = 0;
	//按3种字符的先后顺序枚举
	for(int i = 0; i < 3; i++)
		for(int j = 0; j < 3; j++)
			for (int k = 0; k < 3; k++)
			{
				if (i != j && i != k && j != k)  //枚举到3个位置的字符不同时
					res += cal(i, j, k);           //累计这种先后顺序的可行方案数
			}

	cout << res << endl;
}

signed main()
{
	int t = 1;
	//cin >> t;
	while (t--)
	{
		solve();
	}

	return 0;
}

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
吐槽:开始没想到k可以用2*j-1表示,想着单独处理,奈何能力不够写不出来。题目注意点:

1.当三个数间距相同时,第二个数相当于中点嘛,只要直到两个数的位置即可求出第三个点的位置。

2.注意动态数组用upper_bound()时的形式。第二种方法在确认后两个字符的位置时是存在一个转换的,因为要枚举位置,只能通过枚举存位置的数组的下标来。在存位置的数组中先找到符合的位置,但是返回的是下标,再用数组v[][]得出在s中的位置,进而才能比较。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值