【贪心 树状数组】P9310 [EGOI 2021] Luna likes Love / 卢娜爱磕 cp|普及+
本文涉及知识点
C++贪心
【C++】树状数组的使用、原理、封装类、样例
P9310 [EGOI 2021] Luna likes Love / 卢娜爱磕 cp
题目背景
Day 1 Problem B.
题面译自 EGOI2021 luna。
题目描述
卢娜想出了一个不同寻常的点子。她让 2n2n2n 个朋友排成一条长队,并给他们每人一个 1∼n1\sim n1∼n 的整数。每个整数恰好出现两次。每一对有相同数字的朋友组成一对情侣。
卢娜希望让每一对情侣去一次约会。然而,并没有这么简单。为了让一对情侣去约会,双方在队伍中必须互相紧挨着,也就是说不能有任何人站在他们中间。
卢娜可以进行两种操作:
- 她可以让任意两个紧挨着的人交换位置。
- 如果一对情侣互相紧挨着,卢娜可以让他们去约会。这一对情侣将从队伍中离开,后面的人会补上他们的位置。
所有操作可以以任意的顺序进行。例如,她可以交换几次,然后让几对情侣去约会,再交换几次。
请求出让所有人去约会的最少操作次数。
输入格式
第一行一个整数 nnn。
第二行 2n2n2n 个整数 aia_iai,依次表示队伍中朋友拿到的数字。
输出格式
一行,一个整数,表示最少操作次数。
输入输出样例 #1
输入 #1
3
3 1 2 1 2 3
输出 #1
4
输入输出样例 #2
输入 #2
5
5 1 2 3 2 3 1 4 5 4
输出 #2
7
说明/提示
样例 111 解释
卢娜先让第三个人和第四个人交换位置,得到 3,1,1,2,2,33,1,1,2,2,33,1,1,2,2,3。
然后她可以让数字 111 和 222 的情侣去约会。之后,数字 333 的情侣会互相紧挨着,卢娜可以让他们也去约会。
综上,共需要 444 次操作:一次交换和三次让情侣去约会。
数据范围
对于全部数据,1≤n≤5×1051\le n\le 5\times 10^51≤n≤5×105,1≤ai≤n1\le a_i\le n1≤ai≤n。
- 子任务一(777 分):任意一对情侣都紧挨着,n≤100n\le 100n≤100。
- 子任务二(888 分):任意一对情侣之间至多有一个人,n≤100n\le 100n≤100。
- 子任务三(111111 分):前 nnn 个人的数字构成一个 1∼n1\sim n1∼n 的排列,n≤3×103n\le 3\times 10^3n≤3×103。
- 子任务四(161616 分):前 nnn 个人的数字构成一个 1∼n1\sim n1∼n 的排列。
- 子任务五(222222 分):n≤3×103n\le 3\times 10^3n≤3×103。
- 子任务六(363636 分):无特殊限制。
[EGOI 2021] Luna likes Love / 卢娜爱磕 cp
性质一:有两对情侣,第一对下标是a1, a2,第二对下标是b1, b2。a1<a2, b1<b2, a1<b1。如果两者相离,即a2 <b1。则两者互不影响。
性质二:如果两者相互包含。即b2<a2。先处理第二队可以少操作一次。
性质三:两者相交。那对先,那对后都一样。后操作的对都少一次操作一次。
结论一:除了包含关系,顺序可以是任意。我们从原始距离小的开始处理,可以保证先处理被包含的。
性质四:移动并移除这对情侣后,其他情侣的相对顺序不变。
性质五:移动情侣的次数等于:a2-a1-(a1到a2已经删除的情侣)。bit[i] 表示下标为i的情侣是否被删除。
代码
核心代码
#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>
#include<list>
#include<array>#include <bitset>
using namespace std;template<class T1, class T2>
std::istream& operator >> (std::istream& in, pair<T1, T2>& pr) {in >> pr.first >> pr.second;return in;
}template<class T1, class T2, class T3 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t);return in;
}template<class T1, class T2, class T3, class T4 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t);return in;
}template<class T = int>
vector<T> Read() {int n;cin >> n;vector<T> ret(n);for (int i = 0; i < n; i++) {cin >> ret[i];}return ret;
}
template<class T = int>
vector<T> ReadNotNum() {vector<T> ret;T tmp;while (cin >> tmp) {ret.emplace_back(tmp);if ('\n' == cin.get()) { break; }}return ret;
}template<class T = int>
vector<T> Read(int n) {vector<T> ret(n);for (int i = 0; i < n; i++) {cin >> ret[i];}return ret;
}template<int N = 1'000'000>
class COutBuff
{
public:COutBuff() {m_p = puffer;}template<class T>void write(T x) {int num[28], sp = 0;if (x < 0)*m_p++ = '-', x = -x;if (!x)*m_p++ = 48;while (x)num[++sp] = x % 10, x /= 10;while (sp)*m_p++ = num[sp--] + 48;AuotToFile();}void writestr(const char* sz) {strcpy(m_p, sz);m_p += strlen(sz);AuotToFile();}inline void write(char ch){*m_p++ = ch;AuotToFile();}inline void ToFile() {fwrite(puffer, 1, m_p - puffer, stdout);m_p = puffer;}~COutBuff() {ToFile();}
private:inline void AuotToFile() {if (m_p - puffer > N - 100) {ToFile();}}char puffer[N], * m_p;
};template<int N = 1'000'000>
class CInBuff
{
public:inline CInBuff() {}inline CInBuff<N>& operator>>(char& ch) {FileToBuf();while (('\r' == *S) || ('\n' == *S) || (' ' == *S)) { S++; }//忽略空格和回车ch = *S++;return *this;}inline CInBuff<N>& operator>>(int& val) {FileToBuf();int x(0), f(0);while (!isdigit(*S))f |= (*S++ == '-');while (isdigit(*S))x = (x << 1) + (x << 3) + (*S++ ^ 48);val = f ? -x : x; S++;//忽略空格换行 return *this;}inline CInBuff& operator>>(long long& val) {FileToBuf();long long x(0); int f(0);while (!isdigit(*S))f |= (*S++ == '-');while (isdigit(*S))x = (x << 1) + (x << 3) + (*S++ ^ 48);val = f ? -x : x; S++;//忽略空格换行return *this;}template<class T1, class T2>inline CInBuff& operator>>(pair<T1, T2>& val) {*this >> val.first >> val.second;return *this;}template<class T1, class T2, class T3>inline CInBuff& operator>>(tuple<T1, T2, T3>& val) {*this >> get<0>(val) >> get<1>(val) >> get<2>(val);return *this;}template<class T1, class T2, class T3, class T4>inline CInBuff& operator>>(tuple<T1, T2, T3, T4>& val) {*this >> get<0>(val) >> get<1>(val) >> get<2>(val) >> get<3>(val);return *this;}template<class T = int>inline CInBuff& operator>>(vector<T>& val) {int n;*this >> n;val.resize(n);for (int i = 0; i < n; i++) {*this >> val[i];}return *this;}template<class T = int>vector<T> Read(int n) {vector<T> ret(n);for (int i = 0; i < n; i++) {*this >> ret[i];}return ret;}template<class T = int>vector<T> Read() {vector<T> ret;*this >> ret;return ret;}
private:inline void FileToBuf() {const int canRead = m_iWritePos - (S - buffer);if (canRead >= 100) { return; }if (m_bFinish) { return; }for (int i = 0; i < canRead; i++){buffer[i] = S[i];//memcpy出错 }m_iWritePos = canRead;buffer[m_iWritePos] = 0;S = buffer;int readCnt = fread(buffer + m_iWritePos, 1, N - m_iWritePos, stdin);if (readCnt <= 0) { m_bFinish = true; return; }m_iWritePos += readCnt;buffer[m_iWritePos] = 0;S = buffer;}int m_iWritePos = 0; bool m_bFinish = false;char buffer[N + 10], * S = buffer;
};template<class ELE = int >
class ITreeArrSumOpe
{
public:virtual void Assign(ELE& dest, const ELE& src) = 0;virtual ELE Back(const ELE& n1, const ELE& n2) = 0;
};template<class ELE = int >
class CTreeArrAddOpe :public ITreeArrSumOpe<ELE>
{
public:virtual void Assign(ELE& dest, const ELE& src) {dest += src;}virtual ELE Back(const ELE& n1, const ELE& n2) {return n1 - n2;}
};template<class ELE = int, class ELEOpe = CTreeArrAddOpe<ELE> >
class CTreeArr
{
public:CTreeArr(int iSize) :m_vData(iSize + 1){}void Add(int index, ELE value){if ((index < 0) || (index >= m_vData.size() - 1)) { return; }index++;while (index < m_vData.size()){m_ope.Assign(m_vData[index], value);index += index & (-index);}}ELE Sum(int index)//[0...index]之和{index++;ELE ret = 0;while (index){m_ope.Assign(ret, m_vData[index]);index -= index & (-index);}return ret;}ELE Sum() { return Sum(m_vData.size() - 2); }ELE Get(int index){return m_ope.Back(Sum(index), Sum(index - 1));}
private:ELEOpe m_ope;vector<ELE> m_vData;
};class Solution {
public:long long Ans(const int N, vector<int>& a) {vector<int> inx(N + 1, -1);vector<tuple<int, int, int>> disPeo;for (int i = 0; i < 2 * N; i++) {if (-1 != inx[a[i]]) {disPeo.emplace_back(i - inx[a[i]], inx[a[i]], i);}inx[a[i]] = i;}sort(disPeo.begin(), disPeo.end());CTreeArr<int> bit(2 * N);long long ans = N;for (const auto& [tmp, x, y] : disPeo) {const int need = y - x - 1 - (bit.Sum(y) - bit.Sum(x));assert(need >= 0);ans += need;bit.Add(x, 1);bit.Add(y, 1);}return ans;}
};int main() {
#ifdef _DEBUGfreopen("a.in", "r", stdin);
#endif // DEBUG ios::sync_with_stdio(0); cin.tie(nullptr);CInBuff<> in; COutBuff<10'000'000> ob; int N;in >> N ;auto a = in.Read<int>(2 * N);
#ifdef _DEBUG printf("N=%d", N);Out(a, ",a=");//Out(ab, ",ab=");//Out(B, "B=");//Out(que, "que=");//Out(B, "B=");
#endif // DEBUG auto res = Solution().Ans(N,a);cout << res << "\n";return 0;
};
单元测试
int N;vector<int> a;TEST_METHOD(TestMethod11){N = 3, a = { 3,1,2,1,2,3 };auto res = Solution().Ans(N,a);AssertEx(4LL, res);}TEST_METHOD(TestMethod12){N = 5, a = { 5,1,2,3,2,3,1,4,5,4 };auto res = Solution().Ans(N, a);AssertEx(7LL, res);}
扩展阅读
我想对大家说的话 |
---|
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。 |
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作 |
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注 |
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。 |
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。 |
如果程序是一条龙,那算法就是他的是睛 |
失败+反思=成功 成功+反思=成功 |
视频课程
先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176
测试环境
操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。