<think>题目要求:模拟两人轮流选择化妆品的过程,每次Misserina根据当前指令选择剩余化妆品中时尚值或美丽值最大的那个,ShenTianYi_则选择对应属性(与Misserina选择的属性相同)的最小值。
注意:每次选择后,两个化妆品会被移除(一个被Misserina选走,一个被ShenTianYi_选走)。但是,每个化妆品有两个属性,所以当我们按照一个属性排序并移除时,另一个属性也要相应移除。
然而,直接模拟删除操作(比如使用vector的erase)会导致每次删除的复杂度为O(n),总复杂度为O(n^2),对于n=500000(总共有1000000个元素)显然会超时。
因此,我们需要更高效的方法。
思路:
我们可以预先将化妆品按照时尚值和美丽值分别排序,并记录每个化妆品在另一个数组中的位置(或者使用索引)。但是,由于每次选择后,化妆品会被移除,我们需要维护两个数组(时尚值和美丽值)中哪些元素已经被移除。
另一种思路是使用“双指针”和“懒惰删除”策略(即使用堆或平衡树,但C++标准库中没有内置平衡树,我们可以用set,但set的删除也是O(log n)的,总复杂度O(n log n)可以接受,但常数较大,且需要处理两个属性)。
然而,题目要求n最大50万,总共有100万个元素,使用set的复杂度为O(n log n)是可行的。
具体做法:
1. 我们建立两个multiset(或者set,因为题目说所有值互不相同,所以用set即可)来分别维护时尚值和美丽值,并且每个化妆品用一个结构体表示,包含时尚值、美丽值和唯一标识(比如索引)。
2. 同时,我们还需要建立两个数组,分别按时尚值和美丽值排序,但为了方便快速删除,我们使用两个set,分别按照时尚值和美丽值作为关键字排序。
但是,当我们删除一个化妆品时,需要同时在两个set中删除,这要求我们能够通过一个化妆品对象在两个set中找到并删除。我们可以使用指针或者索引,但是set不支持随机访问。另一种方法是使用两个set,每个set存储化妆品对象的指针,但这样删除时需要在另一个set中查找相同的对象,效率不高。
改进:使用一个set,但是按照两种排序方式分别建立两个set,并存储相同的元素(对象)。这样,当删除时,我们需要在两个set中都删除,但如何关联?我们可以用两个set,每个set存储相同的化妆品对象的指针,然后删除时,通过指针在两个set中删除。但是,这样需要两次删除操作,每次O(log n),总删除操作O(n log n)。
然而,题目要求模拟n轮,每轮操作包括:
- 根据Q_i,在剩余化妆品中,选择时尚值最大(或美丽值最大)的化妆品(Misserina选)。
- 然后,在剩余化妆品中,选择同属性(即如果Misserina按时尚选,则ShenTianYi_按时尚选最小;如果按美丽选,则按美丽选最小)的最小值(ShenTianYi_选)。
所以,我们可以维护两个set:
set_f: 按时尚值排序,存储所有化妆品。
set_b: 按美丽值排序,存储所有化妆品。
但是,当我们删除一个化妆品时,需要在两个set中同时删除。
具体步骤:
1. 初始化:将2n个化妆品加入两个set中。每个化妆品可以用一个结构体表示,包含f(时尚值)、b(美丽值)和id(可选,用于调试,但非必须)。
2. 对于每一轮选择(n轮):
读取Q_i:
如果Q_i==1(按时尚选):
则从set_f中取出最大的元素(即最后一个元素,或者用rbegin()获取)作为Misserina的选择,记为A。
然后,从set_f中取出最小的元素(即第一个元素,begin())作为ShenTianYi_的选择,记为B。
注意:这里ShenTianYi_选的是时尚值最小的,但题目要求是“对应属性”的最小值,即也是时尚属性。
如果Q_i==2(按美丽选):
则从set_b中取出最大的元素作为Misserina的选择,记为A。
然后,从set_b中取出最小的元素作为ShenTianYi_的选择,记为B。
但是,这里有一个问题:当我们从set_f中删除A和B时,也要在set_b中删除A和B。同样,从set_b中删除时,也要在set_f中删除。
因此,每轮操作:
- 根据Q_i选择主set(如果是1,则主set为set_f;如果是2,则主set为set_b)。
- 从主set中取最大元素(Misserina选)和最小元素(ShenTianYi_选)。
- 将这两个元素从两个set中删除。
注意:有可能在取最小元素时,这个元素已经被删除?不会,因为我们是同时从两个set中删除,但这里我们是一次性取两个元素,然后同时删除两个元素(两个化妆品)。
但是,有一个重要问题:当Q_i==1时,我们从set_f中取最大和最小,然后删除这两个化妆品。那么,在set_b中,我们也要删除这两个化妆品。同样,当Q_i==2时,从set_b中取最大和最小,然后删除,同时也要在set_f中删除这两个。
然而,当我们从set_f中取最小元素时,这个元素在set_b中可能不是最小的?但是,题目要求:ShenTianYi_选择的是在剩余化妆品中,与Misserina选择的属性相同的最小值。所以,当Misserina按时尚选时,ShenTianYi_就选剩余中时尚值最小的;当按美丽选时,就选剩余中美丽值最小的。
所以,我们的做法正确。
3. 统计:分别累计Misserina和ShenTianYi_的时尚值和美丽值。
注意:因为每个化妆品有两个属性,所以当我们删除一个化妆品时,它的两个属性都要被计入对应的人。
如何记录?每轮中,Misserina选的化妆品(记为A)的两个属性(fA, bA)都加到Misserina的时尚值和美丽值上;同样,ShenTianYi_选的化妆品(记为B)的两个属性(fB, bB)加到ShenTianYi_的时尚值和美丽值上。
4. 实现细节:我们使用两个set,每个set存储化妆品,但按照不同的属性排序。我们可以定义两个比较结构体。
结构体定义:
struct Item {
long long f, b;
// 为了区分,我们也可以存id,但非必须,因为两个属性都是唯一的,所以可以用属性值来区分?但是有可能两个不同的化妆品具有相同的属性值?题目说任意两个化妆品的时尚值互不相同,美丽值互不相同。所以我们可以用f或b作为唯一标识?但是一个化妆品同时有f和b,所以我们需要同时存储f和b。
// 注意:同一个化妆品在两个set中存储的是相同的f和b,所以我们可以用f和b来比较,但是删除时需要找到对应的元素。
};
但是,set中存储的是元素的副本,所以当我们删除时,需要删除两个set中相同的元素(即相同的化妆品)。我们可以用指针,但这样管理内存复杂。或者,我们可以用数组存储所有化妆品,然后set中存储索引。这样,删除时通过索引找到化妆品,然后删除两个set中的索引。
另一种方法:使用两个set,分别存储指针,但是这样需要自己管理内存,而且容易出错。
更简单的方法:使用两个set,每个set存储pair<属性值, 唯一标识>,其中唯一标识可以是该化妆品的索引(0~2n-1)。这样,当我们从主set中选出一个元素(通过属性值和索引)后,我们可以通过索引去另一个set中删除对应的元素(另一个set中存储的是<另一个属性, 同一索引>)。
具体:
set_f: set<pair<long long, int>> 按f排序(默认按pair的first排序,即按f升序),但我们取最大元素时,用rbegin()。
set_b: set<pair<long long, int>> 按b排序。
同时,我们还需要两个数组:F[2n]和B[2n],存储每个索引对应的f和b值。
操作步骤:
初始化:
输入n,然后输入2n个F[i]和2n个B[i](i从0到2n-1)。
初始化两个set:
set_f: 对于i=0~2n-1,插入{F[i], i}
set_b: 插入{B[i], i}
然后,我们还需要一个数组deleted[2n](或者用两个set来维护已经被删除的索引?)来标记哪些化妆品已经被删除?但这样我们每次删除时,需要删除两个set中的对应元素,所以不能仅仅标记,而是要从set中删除。
对于每一轮:
读入Q(1或2)
如果Q==1:
从set_f中取最后一个元素(最大的f)-> 即rbegin(),得到(f_max, idx1) -> 这是Misserina选的。
从set_f中取第一个元素(最小的f)-> begin(),得到(f_min, idx2) -> 这是ShenTianYi_选的。
然后,将这两个元素从set_f和set_b中删除。注意:删除时,我们根据索引idx1和idx2,在set_b中找到对应的元素(即{B[idx1], idx1}和{B[idx2], idx2})并删除。同样,在set_f中也要删除这两个元素(实际上,我们在set_f中已经知道了这两个元素,但是set_f中我们是通过f值来存储的,所以我们可以直接删除我们取出的这两个元素(通过迭代器或者值)?但是,set_f中我们取第一个元素(begin())的迭代器是begin(),取最后一个元素(rbegin())的迭代器是--end()?注意:rbegin()返回的是反向迭代器,删除时需要转换为正向迭代器。
具体删除:
对于Misserina选的元素(f_max, idx1):
在set_f中,我们可以用迭代器(对于最后一个元素,我们可以用--set_f.end())来删除,但是注意:我们取的时候已经知道它是最后一个,所以我们可以保存这个迭代器。但是,我们取的时候用的是rbegin(),它返回的是反向迭代器,我们可以用base()转换,但容易出错。
另一种方法:我们不用反向迭代器,而是用set的end()迭代器递减来获取最后一个元素。因为set的迭代器是双向迭代器。
步骤:
auto it1 = set_f.end();
it1--; // 指向最后一个元素
auto miss = *it1; // 得到最后一个元素
auto it2 = set_f.begin();
auto shen = *it2;
然后,删除这两个元素:set_f.erase(it1); set_f.erase(it2);
同时,在set_b中,我们需要通过索引miss.idx和shen.idx来删除。所以,我们需要在set_b中删除包含索引miss.idx和shen.idx的元素。但是,set_b是按b值排序的,我们不知道b值,但是我们知道索引。所以,我们需要一个辅助数据结构:记录每个索引在set_b中的位置?或者,我们可以在set_b中搜索pair<B[idx], idx>,因为B[idx]我们知道(通过数组B),所以我们可以用这个pair来删除。
但是,注意:set的删除可以传入值,只要这个值等于set中的某个元素(pair的比较是先比较第一个元素,再比较第二个元素)。所以,我们可以:
set_b.erase({B[miss.idx], miss.idx});
set_b.erase({B[shen.idx], shen.idx});
但是,这样删除的复杂度是O(log n),所以总删除操作是O(log n)。
注意:同样,当Q==2时,我们从set_b中取最大和最小,然后删除,并在set_f中删除对应的元素。
然后,将Misserina选的化妆品的两个属性(f_max, B[miss.idx])加到Misserina的总时尚和总美丽上;将ShenTianYi_选的(f_min, B[shen.idx])加到ShenTianYi_的总时尚和总美丽上。
但是,注意:在Q==1时,Misserina选的是时尚值最大的,所以她的时尚值加上f_max,美丽值加上B[miss.idx];ShenTianYi_选的是时尚值最小的,所以她的时尚值加上f_min,美丽值加上B[shen.idx]。
同样,当Q==2时:
从set_b中取最大(最后一个)和最小(第一个)元素。
miss = 最大元素(b_max, idx1),shen = 最小元素(b_min, idx2)
然后,在set_f和set_b中删除这两个元素(通过索引)。
Misserina的时尚值加上F[idx1](因为时尚值存储在F数组中),美丽值加上b_max;ShenTianYi_的时尚值加上F[idx2],美丽值加上b_min。
注意:删除元素后,这两个化妆品就被移除了,下一轮就不会再出现。
但是,有一个问题:当删除元素时,我们可能会删除同一个元素吗?即Misserina选的和ShenTianYi_选的是同一个?不可能,因为时尚值(或美丽值)互不相同,最大值和最小值一定不同(除非只有一个元素,但每轮开始至少有两个元素)。
另外,每轮删除两个元素,总共n轮,删除2n个元素。
复杂度:每轮操作是O(log n)(因为set的删除和取最大最小都是O(log n)),总复杂度O(n log n),对于n=500000,大约500000*log2(500000)≈500000*19=9.5e6,可以接受。
但是,我们需要注意,在删除时,我们通过索引去另一个set中删除,需要构造一个pair(属性值,索引),而属性值我们通过数组B(或F)得到。所以,我们需要两个数组F和B,大小为2n,存储每个索引对应的时尚值和美丽值。
步骤总结:
1. 输入n,然后输入2n个F[i]和2n个B[i](i从0到2n-1)。
2. 初始化两个set:
set_f: set<pair<long long, int>>,按f升序(默认)
set_b: set<pair<long long, int>>,按b升序
将每个索引i插入两个set:set_f.insert({F[i], i}); set_b.insert({B[i], i});
3. 初始化四个累加变量:long long m_f=0, m_b=0, s_f=0, s_b=0; (分别表示Misserina的时尚值和美丽值,ShenTianYi_的时尚值和美丽值)
4. 循环n次,每次读入一个Q:
if (Q == 1) {
// 从set_f中取最大(最后一个)和最小(第一个)
auto it_miss = set_f.end(); it_miss--; // 指向最后一个元素
auto it_shen = set_f.begin();
// 获取值
int idx_miss = it_miss->second;
int idx_shen = it_shen->second;
// 加到Misserina
m_f += F[idx_miss];
m_b += B[idx_miss];
// 加到ShenTianYi_
s_f += F[idx_shen];
s_b += B[idx_shen];
// 从set_f中删除这两个元素(通过迭代器)
set_f.erase(it_miss);
set_f.erase(it_shen);
// 从set_b中删除这两个元素(通过值:需要构造pair,注意pair的first是B[idx])
set_b.erase({B[idx_miss], idx_miss});
set_b.erase({B[idx_shen], idx_shen});
} else { // Q==2
// 从set_b中取最大(最后一个)和最小(第一个)
auto it_miss = set_b.end(); it_miss--;
auto it_shen = set_b.begin();
int idx_miss = it_miss->second;
int idx_shen = it_shen->second;
m_f += F[idx_miss];
m_b += B[idx_miss];
s_f += F[idx_shen];
s_b += B[idx_shen];
set_b.erase(it_miss);
set_b.erase(it_shen);
// 同时从set_f中删除
set_f.erase({F[idx_miss], idx_miss});
set_f.erase({F[idx_shen], idx_shen});
}
5. 输出:
cout << m_f << " " << m_b << endl;
cout << s_f << " " << s_b << endl;
但是,这里有一个问题:在Q==1时,我们从set_f中删除两个元素,然后从set_b中删除两个元素。但是,在删除set_b中的元素时,我们使用了{B[idx], idx},这个pair在set_b中是存在的,因为一开始就插入了。同样,在Q==2时,我们也是类似操作。
但是,注意:在删除set_b中的元素时,我们使用的是set_b.erase(pair),这个操作会删除与pair相等的元素。因为set中每个元素是唯一的(索引唯一),所以可以正确删除。
但是,有一个潜在的坑:在Q==1时,我们删除的两个元素,有可能在set_b中已经被删除了吗?不会,因为我们是第一次删除这两个元素。
但是,注意:在Q==1时,我们删除的两个元素(idx_miss和idx_shen)可能是同一个化妆品吗?不可能,因为最大值和最小值在剩余化妆品中肯定不同(且剩余化妆品至少有两个)。
然而,还有一种情况:当我们在set_f中删除两个元素时,这两个元素在set_b中对应的元素可能会影响set_b的迭代器吗?不会,因为set_b和set_f是独立的。
但是,在删除时,我们需要注意:在Q==1中,我们先从set_f中删除了两个元素,然后从set_b中删除。同样,在Q==2中,我们先从set_b中删除两个元素,然后从set_f中删除。这样没有问题。
但是,在Q==1中,我们通过迭代器删除了set_f中的两个元素,然后通过值删除set_b中的两个元素。同样在Q==2中,我们通过迭代器删除了set_b中的两个元素,然后通过值删除set_f中的两个元素。
注意:在删除set_f中的元素时,我们使用的是迭代器,而在删除另一个set中的元素时,我们使用的是值(构造pair)。这样是安全的。
但是,在Q==1中,当我们删除set_f中的元素后,set_f的迭代器会失效吗?不会,因为我们删除了两个迭代器指向的元素,但是我们已经保存了它们的索引,所以后续操作不依赖于这两个迭代器。
然而,在删除set_b中的元素时,我们使用的是值,所以不会受到set_f删除的影响。
但是,有一个问题:在Q==1中,我们通过set_f的迭代器删除了元素,然后我们通过索引去set_b中删除。但是,在set_f中删除元素后,这两个元素在set_b中仍然存在,所以我们可以正常删除。
因此,代码可以这样写。
但是,我们需要注意:在删除时,如果set中不存在这个元素,erase会怎样?因为我们的操作是成对的,所以不会出现不存在的情况。
然而,有一个边界情况:在删除set_b中的元素时,我们构造的pair必须和插入时完全一致(包括索引和属性值)。由于属性值在数组中存储,而数组没有被修改,所以属性值正确。索引也正确。
但是,注意:在Q==1中,我们删除的两个元素(idx_miss和idx_shen)可能是同一个吗?不可能,因为最大值和最小值不同(除非只有一个元素,但每轮开始时至少有两个元素)。
所以,我们按照上述步骤编写代码。
但是,注意:在set中,我们存储的是pair,而pair的默认比较是先比较第一个元素(属性值),如果第一个元素相等,再比较第二个元素(索引)。由于属性值互不相同,所以即使两个不同的化妆品有相同的属性值(但题目中属性值互不相同,所以不会相同),所以不会冲突。而且,索引也是唯一的。
因此,代码实现如下:
注意:使用set的end()和begin()迭代器,以及删除操作。
但是,在删除迭代器时,要注意:删除后迭代器失效,所以我们在删除之前需要保存需要的信息(比如索引)。
另外,在删除set_b中的元素时,我们使用erase({B[idx], idx}),这需要O(log n)时间,但总复杂度O(n log n)可以接受。
我们写代码时,注意使用long long。
但是,有一个问题:在Q==1时,我们取set_f的最大和最小,但是注意,set_f中存储的是按f升序排列的,所以begin()是最小,end()-1是最大。
同样,set_b中存储的是按b升序排列。
所以,上述方法正确。
但是,在Q==1时,我们删除的两个化妆品,在set_b中删除时,我们使用的是B[idx](即该化妆品在数组B中的值),这个值在删除前没有改变,所以正确。
最后,输出即可。
但是,我们需要注意:在删除过程中,我们可能会删除一个在另一个set中已经被删除的元素吗?不会,因为每轮我们删除两个不同的化妆品,并且这两个化妆品在之前没有被删除。
因此,代码实现如下:
注意:由于n最大50万,总共有100万个元素,所以我们需要使用set,并注意常数。set的erase和insert都是O(log n),所以总操作次数为4*n=200万次,每次O(log n),总时间大约2000000 * log2(1000000) ≈ 2000000 * 20 = 40000000,即4e7,在C++中应该可以接受。
但是,为了保险,我们可以使用更快的输入输出(如scanf/printf)。
我们使用C++标准输入输出,并关闭同步,或者使用scanf/printf。
由于题目中数据范围较大(10^9),所以使用long long。
代码:
注意:在set中,我们存储的pair是<属性值, 索引>,索引从0开始。
输入输出样例的验证:
输入:n=3, F=[1,7,3,8,9,4], B=[1665,5,8888,3,4,27], Q=[1,1,2]
初始化:
set_f: 按f升序:{1,0}, {3,2}, {4,5}, {7,1}, {8,3}, {9,4} -> 注意:输入是6个数,索引0~5
set_b: 按b升序:{3,3}, {4,4}, {5,1}, {27,5}, {1665,0}, {8888,2}
第一轮:Q=1
从set_f取最大:{9,4} -> Misserina选:时尚值9,美丽值B[4]=4(因为索引4在B数组中是4)
从set_f取最小:{1,0} -> ShenTianYi_选:时尚值1,美丽值B[0]=1665
然后,从set_f中删除这两个元素:删除{1,0}和{9,4},set_f变为:{3,2},{4,5},{7,1},{8,3}
从set_b中删除这两个元素:删除{B[0]=1665,0}即{1665,0}和{B[4]=4,4}即{4,4},set_b变为:{3,3},{5,1},{27,5},{8888,2}
此时,Misserina的时尚值=9,美丽值=4;ShenTianYi_的时尚值=1,美丽值=1665。
第二轮:Q=1
从set_f取最大:{8,3} -> Misserina选:时尚值8,美丽值B[3]=3
从set_f取最小:{3,2} -> ShenTianYi_选:时尚值3,美丽值B[2]=8888
删除:set_f中删除{3,2}和{8,3},set_f变为:{4,5},{7,1}
set_b中删除{B[3]=3,3}即{3,3}和{B[2]=8888,2}即{8888,2},set_b变为:{5,1},{27,5}
此时,Misserina累计:时尚值9+8=17,美丽值4+3=7;ShenTianYi_累计:1+3=4,美丽值1665+8888=10553。
第三轮:Q=2
从set_b中取最大:{27,5} -> Misserina选:美丽值27,时尚值F[5]=4
从set_b中取最小:{5,1} -> ShenTianYi_选:美丽值5,时尚值F[1]=7
删除:set_b中删除{5,1}和{27,5},set_b为空。
set_f中删除{F[5]=4,5}即{4,5}和{F[1]=7,1}即{7,1},set_f为空。
Misserina累计:时尚值17+4=21,美丽值7+27=34;ShenTianYi_累计:时尚值4+7=11,美丽值10553+5=10558。
输出:
第一行:21 34
第二行:11 10558
但是,样例输出第二行是11 10558,而我们的计算中ShenTianYi_的美丽值10558,但样例输出是10558?而样例输出是10558,但是我们的计算过程:第一轮1665,第二轮8888,第三轮5,总和=1665+8888+5=10558,正确。
但是,注意:样例输出第二行是“11 10558”,而我们计算ShenTianYi_的时尚值:1+3+7=11,美丽值:1665+8888+5=10558。
所以,输出正确。
因此,我们按照这个思路编写代码。
但是,注意:在删除时,我们使用set_b.erase({B[idx], idx}),这里B[idx]是数组中的值,而idx是索引,所以正确。
代码实现:
注意:使用set时,迭代器删除后,该迭代器失效,所以我们在删除前已经保存了索引,所以没有问题。
另外,在循环中,我们每次删除两个元素,共n轮,删除2n个元素。
但是,在Q==1时,我们删除了set_f中的两个元素,然后删除了set_b中的两个元素。在Q==2时,我们删除了set_b中的两个元素,然后删除了set_f中的两个元素。
所以,我们需要两个set:set_f和set_b。
代码:
#include <iostream>
#include <set>
#include <vector>
#include <cstdio>
using namespace std;
typedef long long LL;
typedef pair<LL, int> PLI; // 存储(属性值,索引)
int main() {
int n;
scanf("%d", &n);
int m = 2 * n;
vector<LL> F(m), B(m);
for (int i = 0; i < m; i++) {
scanf("%lld", &F[i]);
}
for (int i = 0; i < m; i++) {
scanf("%lld", &B[i]);
}
vector<int> Q(n);
for (int i = 0; i < n; i++) {
scanf("%d", &Q[i]);
}
set<PLI> set_f, set_b;
for (int i = 0; i < m; i++) {
set_f.insert({F[i], i});
set_b.insert({B[i], i});
}
LL m_f = 0, m_b = 0; // Misserina的时尚值和美丽值
LL s_f = 0, s_b = 0; // ShenTianYi_的时尚值和美丽值
for (int i = 0; i < n; i++) {
if (Q[i] == 1) {
// 从set_f中取最大(最后一个)和最小(第一个)
auto it_miss = set_f.end();
it_miss--; // 指向最后一个元素
auto it_shen = set_f.begin();
int idx_miss = it_miss->second;
int idx_shen = it_shen->second;
// Misserina选的是最大的时尚值,所以加上这个化妆品的时尚值和美丽值
m_f += F[idx_miss];
m_b += B[idx_miss];
// ShenTianYi_选的是最小的时尚值,加上这个化妆品的时尚值和美丽值
s_f += F[idx_shen];
s_b += B[idx_shen];
// 从set_f中删除这两个元素(通过迭代器)
set_f.erase(it_miss);
set_f.erase(it_shen);
// 从set_b中删除这两个元素(通过值)
set_b.erase({B[idx_miss], idx_miss});
set_b.erase({B[idx_shen], idx_shen});
} else { // Q[i]==2
// 从set_b中取最大(最后一个)和最小(第一个)
auto it_miss = set_b.end();
it_miss--;
auto it_shen = set_b.begin();
int idx_miss = it_miss->second;
int idx_shen = it_shen->second;
m_f += F[idx_miss];
m_b += B[idx_miss];
s_f += F[idx_shen];
s_b += B[idx_shen];
// 从set_b中删除
set_b.erase(it_miss);
set_b.erase(it_shen);
// 从set_f中删除
set_f.erase({F[idx_miss], idx_miss});
set_f.erase({F[idx_shen], idx_shen});
}
}
printf("%lld %lld\n", m_f, m_b);
printf("%lld %lld\n", s_f, s_b);
return 0;
}
但是,我们需要注意:在删除时,我们可能遇到这样的情况:在Q==1中,我们删除set_f中的两个元素后,再删除set_b中的两个元素。但是,有可能在删除set_b中的元素时,其中一个元素已经被删除了?不会,因为每轮我们删除两个不同的元素,而且这两个元素在之前没有被删除过。
所以,我们提交代码。
但是,注意:在删除set_b中的元素时,我们构造的pair必须和插入时完全一致。由于在数组中,B[idx]的值在删除过程中不会改变,所以正确。
但是,有一个潜在的问题:在Q==1时,我们删除set_f中的两个元素后,这两个元素在set_b中仍然存在,所以可以删除。同样,在Q==2时,我们先删除了set_b中的两个元素,然后删除set_f中的两个元素,这两个元素在set_f中仍然存在。
所以,没有问题。
但是,我们考虑一个极端情况:在Q==1中,我们删除的两个元素,在set_b中删除时,其中一个元素可能已经被删除了吗?不会,因为我们是第一次删除这两个元素。
因此,我们按照上述代码编写。
但是,在测试样例中,我们得到了正确结果,所以我们可以提交。
注意:在C++中,set的end()迭代器指向的是最后一个元素的下一个位置,所以递减一次就得到最后一个元素。
另外,在删除set_b中的元素时,我们使用{ B[idx], idx },由于B[idx]是数组中的值,而数组在初始化后没有改变,所以正确。
但是,我们需要注意:在删除set_b中的元素时,如果set_b中不存在这个元素,erase不会报错,但是会返回0(表示删除的元素个数),但这里我们确保存在,所以不会出错。
因此,我们运行代码并测试样例。
样例输入:
3
1 7 3 8 9 4
1665 5 8888 3 4 27
1 1 2
输出:
21 34
11 10558
但是,我们计算一下:
Misserina:
时尚值:9(第一轮)+8(第二轮)+4(第三轮)=21
美丽值:4(第一轮)+3(第二轮)+27(第三轮)=34
ShenTianYi_:
时尚值:1+3+7=11
美丽值:1665+8888+5=10558
所以输出正确。
但是,注意:第三轮Misserina选的是美丽值最大的,即27(索引5,时尚值为4),ShenTianYi_选的是美丽值最小的,即5(索引1,时尚值为7)。
因此,我们按照这个代码提交。
但是,由于n最大50万,我们需要注意常数。使用set,每个操作O(log n),总操作次数4*n=200万次,所以总时间大约是2000000 * log2(1000000) ≈ 2000000 * 20 = 40000000,即4e7,在C++中应该可以在2秒内完成(题目2.48秒)。
但是,我们使用scanf/printf来避免输入输出瓶颈。
最后,注意:在删除时,我们使用set_f.erase(iterator)和set_f.erase(iterator)两次,而set_b.erase(value)两次,每次erase是O(log n),所以总复杂度O(n log n)。
提交代码。
但是,我们可能遇到一个问题:在Q==1时,我们删除了set_f中的两个元素,然后删除set_b中的两个元素。但是,在删除set_b中的元素时,我们传入的pair是{B[idx], idx},而set_b中存储的pair的first是B[idx],所以可以匹配。但是,如果两个不同的化妆品具有相同的B值?题目说美丽值互不相同,所以不会相同。同样,时尚值也互不相同。
所以,没有问题。
因此,我们编写完整代码。</think>为了解决这个问题,我们需要模拟Misserina和ShenTianYi_两人轮流选择化妆品的过程。每次Misserina根据给定的指令选择剩余化妆品中时尚值或美丽值最大的那个,而ShenTianYi_则选择同属性值最小的那个。我们需要高效地处理每轮选择,并统计两人最终获得的时尚值和美丽值总和。
### 方法思路
1. **数据结构选择**:使用两个`set`(集合)分别按时尚值和美丽值存储化妆品。每个化妆品表示为`pair<属性值, 索引>`,以便快速访问和删除。
2. **选择过程**:
- **当指令为1(时尚值优先)**:从时尚值集合中取出最大值(Misserina选择)和最小值(ShenTianYi_选择),然后从两个集合中删除这两个化妆品。
- **当指令为2(美丽值优先)**:从美丽值集合中取出最大值(Misserina选择)和最小值(ShenTianYi_选择),然后从两个集合中删除这两个化妆品。
3. **统计结果**:在每轮选择中,将选中的化妆品属性值分别累加到两人的时尚值和美丽值总和中。
### 解决代码
```cpp
#include <iostream>
#include <set>
#include <vector>
#include <cstdio>
using namespace std;
typedef long long LL;
typedef pair<LL, int> PLI;
int main() {
int n;
scanf("%d", &n);
int m = 2 * n;
vector<LL> F(m), B(m);
for (int i = 0; i < m; i++) {
scanf("%lld", &F[i]);
}
for (int i = 0; i < m; i++) {
scanf("%lld", &B[i]);
}
vector<int> Q(n);
for (int i = 0; i < n; i++) {
scanf("%d", &Q[i]);
}
set<PLI> set_f, set_b;
for (int i = 0; i < m; i++) {
set_f.insert({F[i], i});
set_b.insert({B[i], i});
}
LL m_f = 0, m_b = 0;
LL s_f = 0, s_b = 0;
for (int i = 0; i < n; i++) {
if (Q[i] == 1) {
auto it_miss = set_f.end();
it_miss--;
auto it_shen = set_f.begin();
int idx_miss = it_miss->second;
int idx_shen = it_shen->second;
m_f += F[idx_miss];
m_b += B[idx_miss];
s_f += F[idx_shen];
s_b += B[idx_shen];
set_f.erase(it_miss);
set_f.erase(it_shen);
set_b.erase({B[idx_miss], idx_miss});
set_b.erase({B[idx_shen], idx_shen});
} else {
auto it_miss = set_b.end();
it_miss--;
auto it_shen = set_b.begin();
int idx_miss = it_miss->second;
int idx_shen = it_shen->second;
m_f += F[idx_miss];
m_b += B[idx_miss];
s_f += F[idx_shen];
s_b += B[idx_shen];
set_b.erase(it_miss);
set_b.erase(it_shen);
set_f.erase({F[idx_miss], idx_miss});
set_f.erase({F[idx_shen], idx_shen});
}
}
printf("%lld %lld\n", m_f, m_b);
printf("%lld %lld\n", s_f, s_b);
return 0;
}
```
### 代码解释
1. **输入处理**:读取化妆品数量`n`,以及每个化妆品的时尚值`F`和美丽值`B`,还有每轮的选择指令`Q`。
2. **集合初始化**:创建两个集合`set_f`和`set_b`,分别按时尚值和美丽值升序存储化妆品(使用`pair<属性值, 索引>`)。
3. **选择过程**:
- **指令1**:从`set_f`中取最大(最后一个元素)和最小(第一个元素)的时尚值化妆品,分别累加到Misserina和ShenTianYi_的总和,然后从两个集合中删除这两个化妆品。
- **指令2**:从`set_b`中取最大和最小的美丽值化妆品,同样累加并删除。
4. **输出结果**:打印两人最终的时尚值和美丽值总和。
这种方法高效地处理了每轮选择,利用`set`的自动排序和快速删除特性,确保总时间复杂度为O(n log n),适合处理大规模数据。
---
**相关问题**:
1. C++中`set`的底层实现是什么?
2. 如何高效地删除`set`中的最大和最小元素?
3. 在C++中,`pair`是如何进行比较的?
4. 如果属性值有重复,上述方法需要如何调整?