欢迎光临
我们一直在努力

CF2200 Codeforces Round 1084 (Div. 3) 题解 图解 超详细版

A. Eating Game

题意:

n(1≤n≤10)n(1 \\leq n \\leq 10)n(1n10)位玩家围绕桌子玩游戏,第i位玩家有ai(1≤ai≤10)a_i(1 \\leq a_i \\leq 10)ai(1ai10)道菜可以吃。玩家轮流吃菜,轮到第iii位玩家时,如果他还有剩余的菜,那么他需要吃一道菜,然后第 (i mod n)+1(i \\bmod n)+1(imodn)+1 名玩家继续,直到所有的菜都被吃完,吃到最后一道菜的玩家被认为是赢家,确定可能成为赢家的数量。

题解:

容易发现,最后成为赢家的一定是菜数量最多的玩家,因为游戏是按顺序来的,每转一圈,所有玩家的菜数减一,换句话说,如果一个玩家有aia_iai道菜,那么至少需要转ai−1a_i-1ai1圈(在这个玩家处开始,可以少转一圈),假设只有一个玩家有着最多的菜数,那么无论如何一定是他赢。

在这里插入图片描述

如果多个玩家有相同的菜数,那么谁会赢呢?假设他们分别是p1,p2,p3…{p_1,p_2,p_3…}p1,p2,p3,菜数是相同的ap1a_{p_1}ap1,我们可以指定某个玩家获胜,例如让p1p_1p1获胜:我们只需令开始的人是p1p_1p1后面那个,那么转ap1a_{p_1}ap1圈后,最后一个吃菜的人一定是p1p_1p1,并且其余的p2,p3…{p_2,p_3…}p2,p3一定都把ap1a_{p_1}ap1道菜吃完了。

在这里插入图片描述

所以我们可以任意指定最多菜数的人获胜,故可能成为赢家的玩家数量就是最多菜数的玩家数量。

代码

#include <bits/stdc++.h>
using namespace std;
void work()
{
/*
n: 人数
INF: int的无穷大
maxval: 记录最多的菜数是几
maxcnt: 记录最多菜数出现了几次
*/

int n;
const int INF = 0x7f7f7f7f;
int maxval = INF, maxcnt = 0;

// 读入
scanf("%d", &n);

for (int i = 1; i <= n; i++)
{//边读入边处理,不需要保存
int t;
scanf("%d", &t);
if (t > maxval)
{// 超过了历史最大值,更新maxval并重置maxcnt
maxval = t;
maxcnt = 1;
}
else if (t == maxval)
{// 等于历史最大值, maxcnt++
maxcnt++;
}
// 小于历史最大值没有用
}

// 输出
printf("%d\\n", maxcnt);
return;
}
int main()
{
/*T: 共有T组数据*/
int T;
scanf("%d", &T);
while (T)
{
work();
}
return 0;
}

B. Deletion Sort

题意:

AksLolCoding 正在对一个由 n 个正整数组成的数组 a 进行游戏。每一轮操作遵循以下规则:

  • 如果数组 a 是非递减的,游戏结束。
  • 否则,AksLolCoding 可以选择数组中的任意一个元素并将其移除。

请你求出游戏结束时,数组中可能剩余的元素数量的最小值。

补充说明:若对于数组所有满足 1≤i≤m−1 的下标 i,都有 ai​≤ai+1​(其中 m 是当前数组的长度),则称该数组是非递减的。

题解:

本题关键是想到:我们可以保留一个长度为2的递减子序列,删除其他元素,最后两个元素二选一删除即可,这样剩余元素数量为1。(不可能全部删完,因为长度为一的数组一定是非递减的,游戏会直接结束)

示例 {2,4,3,5,7,11,9}\\{2,4,3,5,7,11,9\\}{2,4,3,5,7,11,9}

在这里插入图片描述

当然,如果数组一开始就是非递减的,我们就没有操作的机会了,直接游戏结束。

代码:

#include <bits/stdc++.h>
using namespace std;
void work()
{
/*
n: 数组数字个数
la: 记录上个读入数字的大小
flag: 标记数组是否存在递减子段
*/

int n;
int la = 0;
bool flag = false;

scanf("%d", &n);
for (int i = 1; i <= n; i++)
{// 这个可以边读入边处理,因为只和上一个元素比大小就可以判断递减
int t;
scanf("%d", &t);
if (t < la)
{// 发现递减
flag = true;
la = t;
}
else
{// 非递减,更新下la
la = t;
}
}

// 根据flag的值分类输出答案
if (flag == true) // 一定可以删掉其他的非递减,最后二选一删一个
printf("1\\n");
else // 没操作空间了,直接输出数组长度
printf("%d\\n", n);
return;
}
int main()
{
int T;
scanf("%d", &T);
while (T)
{
work();
}
return 0;
}

C. Specialty String

题意:

AksLolCoding 在玩一个关于长度为 n(1≤n≤5000)n(1 \\leq n \\leq 5000)n(1n5000) 的字符串 s 的游戏。一开始,字符串 s 只由小写英文字母组成。

每一回合,AksLolCoding 可以选择一对下标 (i,j) 满足:

  • 1≤i<j≤n1≤i<j≤n1i<jn
  • si​=sj​≠∗s_i​=s_j​\\neq∗si=sj=
  • 且在 i 和 j 之间的所有字符 sk​(i<k<j)s_k​(i<k<j)ski<k<j都必须是 ∗

如果不存在这样的 (i,j)(i,j)(i,j),游戏结束。否则,AksLolCoding 会把 sis_isi​ 和 sjs_jsj​ 都变成 ∗。游戏结束后,当且仅当字符串里所有字符都变成 ∗ 时,AksLolCoding 获胜。

请你判断:AksLolCoding 是否有可能获胜。

注:∗ 就是 ASCII 码为 42 的星号字符。

题解:

用栈模拟即可,入栈时若和栈顶字符一致则弹出,如果最后栈是空的则获胜,否则不获胜。

在这里插入图片描述

考虑一下为什么这样可行:如果存在子串长这个样子->abababababab,一定没法消掉,能消的只有abbaabbaabba这样嵌套的,或者aaaaaaaaaaaa这样多个相同的。对于前者,消除顺序固定;对于后者,虽然消除顺序多样,但题目只要求判断是否获胜,所以用栈模拟,找出一种可行的消除顺序即可。

代码

#include <bits/stdc++.h>
using namespace std;
void work()
{
/*
n: 字符串长度
c: 读入的单个字符
stk: 处理栈,相同字符弹出,否则压入
*/

int n;
char c;
stack<char> stk;

scanf("%d", &n);
getchar(); // 注意读掉换行符
while (n)
{
c = getchar();
if (!stk.empty() && stk.top() == c) // 栈非空且和栈顶相同
stk.pop();
else // 否则压入
stk.push(c);
}
if (stk.size() == 0) // 栈空说明所有都配对了
printf("yes\\n");
else
printf("no\\n");
return;
}
int main()
{
int T;
scanf("%d", &T);
while (T)
{
work();
}
return 0;
}

D. Portal

题意:

给定一个长度为 nnn 的排列∗^\\text{∗} ppp。在位置 xxxyyy 处各有一个传送门(x<yx < yx<y)。

位于位置 iii 的传送门,初始时在数组的第 iii 个元素和第 i+1i+1i+1 个元素之间。特别地:

  • i=0i=0i=0,传送门在数组第一个元素之前;
  • i=ni=ni=n,传送门在数组最后一个元素之后。

你可以任意多次执行以下两种操作之一:

  • 将某个传送门左侧紧邻的元素移除,并插入到另一个传送门右侧紧邻的位置。
  • 将某个传送门右侧紧邻的元素移除,并插入到另一个传送门左侧紧邻的位置。
  • O\\mathbf{\\color{red}{\\mathcal{O}}}O 表示传送门。例如,若 p=[3,O,4,O]p = [3,\\mathbf{\\color{red}{\\mathcal{O}}},4,\\mathbf{\\color{red}{\\mathcal{O}}}]p=[3,O,4,O]

    • 对左、右传送门分别使用操作 1,得到数组 [O,4,O,1][\\mathbf{\\color{red}{\\mathcal{O}}},4,\\mathbf{\\color{red}{\\mathcal{O}}},1][O,4,O,1][3,O,2,O][3,\\mathbf{\\color{red}{\\mathcal{O}}},2,\\mathbf{\\color{red}{\\mathcal{O}}}][3,O,2,O]
    • 对左、右传送门分别使用操作 2,得到数组 [3,O,2,O][3,\\mathbf{\\color{red}{\\mathcal{O}}},2,\\mathbf{\\color{red}{\\mathcal{O}}}][3,O,2,O][3,1,O,4,O][3,1,\\mathbf{\\color{red}{\\mathcal{O}}},4,\\mathbf{\\color{red}{\\mathcal{O}}}][3,1,O,4,O]

    求使用这些操作能得到的字典序最小†^\\text{†} 的排列。注意:传送门不影响排列的字典序比较。

    ∗^\\text{∗} 长度为 nnn 的排列是一个包含 111nnn 每个整数恰好一次的数组。

    †^\\text{†} 排列 aaa 字典序小于排列 bbb,当且仅当存在下标 iii,使得对所有 1≤j<i1 \\le j < i1j<i 都有 aj=bja_j = b_jaj=bj,且 ai<bia_i < b_iai<bi

    题解:

    容易发现,传送门将数组分成了里外两部分,两种操作不会将数组元素跨部分移动。对于里面部分,元素顺序可以滚动,对于外面部分,元素相对顺序不会改变,但可以调整里部分在数组中的位置。

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    我们的目标是使整体字典序最小。考虑一下,其实就是保证里部分字典序最小,再挑一个外部分最合适的位置将里部分插入。从左到右遍历外部分,里部分的首元素首次小于外部分的元素时,我们就找到了最优插入位置。

    在这里插入图片描述

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    void work()
    {
    /*
    n: 排列长度
    x: 第一个传送门的位置
    y: 第二个传送门的位置
    INF: int的无穷大
    vec: 读入的排列
    veca: x左y右的部分,即外部分
    vecb: x有y左的部分,即里部分
    minval_b: vecb中的最小值
    minpos_b: vecb中最小值的位置
    flag: 判断是否输出过vecb部分
    */

    int n, x, y;
    const int INF = 0x7f7f7f7f;
    int minval_b = INF, minpos_b = 0;
    bool flag = false;

    // 读入
    scanf("%d%d%d", &n, &x, &y);
    vector<int> vec(n + 1), veca, vecb;
    for (int i = 1; i <= n; i++)
    {
    scanf("%d", &vec[i]);
    }

    // 拆分为两个部分
    for (int i = 1; i <= n; i++)
    {
    if (i <= x || i > y)
    veca.push_back(vec[i]);
    else
    {
    vecb.push_back(vec[i]);
    if (minval_b > vec[i])
    { // 记录一下最小值及其位置
    minval_b = vec[i];
    minpos_b = vecb.size() 1;
    }
    }
    }

    // 遍历a部分,看时机输出b部分
    for (int i = 0; i < veca.size(); i++)
    {
    if (flag == false && veca[i] > minval_b)
    { // b部分没输出过,并且当前的a值大于b部分最小值
    flag = true; // 标记输出过了
    for (int j = 0; j < vecb.size(); j++)
    {
    printf("%d ", vecb[(j + minpos_b) % vecb.size()]);
    }
    }
    printf("%d ", veca[i]);
    }
    if (flag == false)
    { // 如果没输出过b部分,在最后输出一下
    for (int j = 0; j < vecb.size(); j++)
    {
    printf("%d ", vecb[(j + minpos_b) % vecb.size()]);
    }
    }
    printf("\\n");
    }
    int main()
    {
    int T;
    scanf("%d", &T);
    while (T)
    {
    work();
    }
    return 0;
    }

    E. Divisive Battle

    题意:

    爱丽丝和鲍勃在一个初始包含 nnn 个正整数的数组 aaa 上玩游戏,爱丽丝先手。

    每一轮玩家操作时:

    • 如果数组 aaa 非递减,游戏立即结束。
    • 否则,该玩家可以选择数组中的一个元素 xxx,并选择满足 1<y,z<x1 < y,z < x1<y,z<xx=yzx=yzx=yz 的正整数 y,zy,zy,z,将 xxx 替换为两个数 yyyzzz(可按任意顺序放在原位置)。
    • 若无法进行任何操作,游戏结束。

    游戏结束时:

    • 如果数组 aaa 是非递减的,则 鲍勃获胜。
    • 否则 爱丽丝获胜。

    假设两人都采取最优策略,判断谁会获胜。

    ∗^* 非递减的定义:对所有 1≤i≤m−11\\le i\\le m-11im1ai≤ai+1a_i\\le a_{i+1}aiai+1,其中 mmm 是数组当前长度。

    题解:

    因为数组非递减就立刻结束游戏,所以开始时数组如果是非递减,则直接判Bob获胜。否则考虑是否存在数字可以拆成两个因子相乘,容易发现,如果包含不同大小的质因子,可以将含大质因子的因子放在前面,含小质因子的因子放在后面,假设游戏能持续进行,最后拆到不能再拆,大质因子位置肯定比小质因子靠前,一定不满足数组非递减,那么Alice获胜。但是考虑到拆开的两个因子可能直接让Bob获胜了,例如

    {1、70、11}⟶{1、7、10、11}
    \\{1、70、11\\}\\longrightarrow\\{1、7、10、11\\}
    {17011}{171011}

    我们可以单独拆一个最小质因子到右边,剩的都放左边,这样无论怎么拆,最后肯定是Alice获胜。

    {1、70、11}⟶{1、35、2、11}
    \\{1、70、11\\}\\longrightarrow\\{1、35、2、11\\}
    {17011}{135211}

    以上是存在数字包含不同质因子的情况,假设所有数字都是唯一质因子。那么除非最后拆完是非递增的,否则Alice获胜。

    {4、16、9}⟶{2、2、4、4、3、3}
    \\{4、16、9\\} \\longrightarrow \\{2、2、4、4、3、3\\}
    {4169}{224433}

    有同学可能会考虑到下面这样的情况,看起来是Bob获胜

    {4、16、9}⟶{4、4、4、9}
    \\{4、16、9\\} \\longrightarrow \\{4、4、4、9\\}
    {4169}{4449}

    但我们可以通过Alice的操作来避免这样情况的出现,只需Alice每一步先从最后一个可拆的数字中拆一个质因子出来即可。

    {4、16、9}⟶{4、16、3、3}
    \\{4、16、9\\} \\longrightarrow \\{4、16、3、3\\}
    {4169}{41633}

    这样无论Bob怎么操作都不可能获胜了。

    代码:

    注:我的写法把各种情况混合在一起判断了,逻辑比较复杂,称不上好代码,如果看不懂可以分情况讨论,把情况分开写,会更好理解一些。

    #include <bits/stdc++.h>
    using namespace std;
    void work(){
    /*
    n: 数组长度
    flag: 标记存在一个数有多个质因子,或分解成质因子的最终序列不是非递减的
    flag1: 标记最开始的数组是否是非递减的
    la: 记录读入初始数组过程的上一个数字是几
    vec: 所有数字分解成质因子后组成的数组
    */

    int n;
    bool flag = false;
    bool flag1 = false;
    int la = 0;

    // 读入
    scanf("%d",&n);
    vector<int>vec,vec1(n+1);
    for(int i = 1;i <= n;i ++){ // 读入时顺便判断一下是否是非递减的
    scanf("%d",&vec1[i]);
    if(vec1[i] < la) flag1 = true;
    else la = vec1[i];
    }
    if(flag1 == false){ // 一打开始就是非递减的,直接Bob获胜
    printf("Bob\\n");
    return ;
    }
    for(int i = 1;i <= n;i ++){ // 依次判断每个数字,判断是否存在数字有至少两个质因子
    int t = vec1[i];
    int cnt = 0; // 记录质因子的个数
    for(int j = 2;j*j <= t;j ++){
    if(t%j == 0){
    vec.push_back(j); // 将分解出的质因子放到vec中
    cnt ++; // 质因子个数加一
    while(t%j == 0) // 把当前因子除完
    t /= j;
    }
    }
    if(t != 1){ // 如果最后不是1,说明还有一个质因子
    vec.push_back(t);
    cnt ++;
    }
    if(cnt > 1) flag = true; // 质因子个数大于1个
    if(cnt == 0) vec.push_back(1); // 说明这个数字本来就是1,也得特判一下,加入vec中
    }
    for(int i = 1;i < int(vec.size());i ++){ // 判断最后的vec是否是非递减的
    if(vec[i] < vec[i1]) flag = true;
    }
    if(flag == true) printf("Alice\\n");
    else printf("Bob\\n");

    }
    int main(){
    int T;
    scanf("%d",&T);
    while(T ){
    work();
    }
    return 0;
    }

    附上官方题解的代码

    #include <bits/stdc++.h>
    using namespace std;

    #define all(x) begin(x), end(x)

    int primebase(int x) {
    set<int> s;
    for (int i = 2; i*i <= x; i++) {
    while (x % i == 0) {
    s.insert(i);
    x /= i;
    }
    }
    if (x > 1) s.insert(x);
    if (s.size() > 1) return 1;
    if (s.size() == 0) return 1;
    return *s.begin();
    }

    void solve() {
    int n;
    cin >> n;
    vector<int> a(n);
    for (auto &i: a) cin >> i;

    // solve
    vector<int> b(n);
    for (int i = 0; i < n; i++) b[i] = primebase(a[i]);
    if (is_sorted(all(a))) {
    cout << "Bob\\n";
    } else if (*min_element(all(b)) == 1) {
    cout << "Alice\\n";
    } else if (is_sorted(all(b))) {
    cout << "Bob\\n";
    } else {
    cout << "Alice\\n";
    }
    }

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

    F. Mooclear Reactor 2

    题意:

    Bessie 需要在她的核反应堆中产生尽可能多的能量。她有 n(1≤n,m≤2∗105)n(1\\leq n,m \\leq 2*10^5)n(1n,m2105) 种不同的粒子。

    每个粒子由两个整数 xxxyyy (1≤x≤109,0≤y≤n)(1 \\leq x \\leq 10^9, 0 \\leq y \\leq n)(1x109,0yn)表示。该粒子产生 xxx 单位能量,但具有反应值 yyy,意味着它最多只能与反应堆中至多 yyy 个其他粒子共存。形式化地说,如果选择该粒子来产生能量,那么除它自身外,最多只能选择 yyy 个粒子一同产生能量。

    Bessie 必须选出满足该约束的一个粒子子集来产生能量。产生的总能量为子集中所有粒子的能量之和。

    商店里有 mmm 个粒子。Bessie 必须从商店恰好购买一个粒子。对商店中的每个粒子,分别求出:如果 Bessie 只购买该粒子,她能产生的最大总能量。Bessie 不一定要使用从商店购买的那个粒子。

    题解:

    本题是一个选粒子问题,有两个限制,第一是总能量最大,第二是每个粒子都有共存粒子数量限制,另外还有个商店粒子混淆视听。其实商店粒子影响不大,因为最多选一个,所以最多在解集中替换掉一个已有粒子。

    先简化一下问题,不考虑商店粒子的影响,并假设所有粒子的能量相同且为1,为使能量最大,应该怎么办?只需按反应值从大到小一个个选,直至选到第i个粒子时,发现已选粒子个数 > yi+1y_i+1yi+1,那么就达到能量最大的情况了。但能量大小不是1,而是1≤x≤1091 \\leq x \\leq 10^91x109,这意味着能量大的粒子可能反应值小,反应值大的粒子可能能量小,二者相互钳制难以判定选择谁,怎么办呢?

    在这里插入图片描述

    我们可以考虑限制粒子数,这样就好实现能量最大化了,如何限制呢?假设我们知道最优选法选了k个粒子,那我们就可以确定候选粒子(即哪些粒子有可能组成包含k个粒子的最优选法):所有y≥k−1y \\geq k-1yk1的粒子都是可以选的,从中选出能量最大的k个即可;所有y<k−1y < k-1y<k1的都是不能取的,因为取了就会使我们的粒子个数受限,从而达不到假设最优解的k个。

    在这里插入图片描述

    因为我们不知道最优选法选了几个粒子,所以分别求出选k(1≤k≤n)(1\\leq k \\leq n)(1kn)个粒子的最大能量,再从这些最大能量中取一个最大值maxnmaxnmaxn,就是我们要的全局最大总能量了。具体算法是用小根堆维护候选集中最大能量的k个粒子,随着指定的k减小,候选集不断增大(因为只有yi+1>=ky_i+1 >= kyi+1>=k的粒子才可以选,k越小,满足的yiy_iyi就越多),保证堆的大小不超过k,超过了就把最小能量的堆顶弹出,实时维护堆中所有粒子能量的和即可,时间复杂度为O(nlogn)O(nlogn)O(nlogn)

    在这里插入图片描述

    在这里插入图片描述

    那加上商店粒子呢?假设商店粒子的能量为xshopx_{shop}xshop,反应值为yshopy_{shop}yshop。如果不选择商店粒子,最大总能量就是上面求出的maxn,如果选商店粒子,问题就转换为拿着一个指定的商店粒子,再从已有粒子中选出k(0≤k≤yshop)(0\\leq k\\leq y_{shop})0kyshop)个粒子,使得总能量最大。这个问题和我们前文中处理的问题很相似,只需假设最后从已有粒子中选了k(0≤k≤yshop0 \\leq k \\leq y_{shop}0kyshop)个粒子,并保证留一个位置给商店粒子,也就是保证yi>=ky_i >= kyi>=k(选k个,要留k+1个位置),再从这yshop+1y_{shop}+1yshop+1种可能中取个最大值,就是我们的答案。

    在这里插入图片描述

    你可能会发现,如果对于每个商店粒子,都O(nlogn)O(nlogn)O(nlogn)跑一遍,时间复杂度是O(n2logn)O(n^2logn)O(n2logn)的。但实际上我们为商店粒子跑的每一遍都和商店粒子本身性质无关,只是保证空出一个位置的前提下,找k个最大能量的已有粒子,并取能量的最大值。所以我们完全可以不管商店粒子,直接指定从已有粒子中选k-1(0≤k≤n0 \\leq k \\leq n0kn)个,要求这些粒子满足yi≥k−1y_i \\geq k-1yik1,和不考虑商店粒子时流程的唯一区别就是要求粒子的yiy_iyi要大1,留一个空位给商店粒子。这样跑一遍把每次的最大能量存到数组f[i]f[i]f[i]中,再对f[i]f[i]f[i]求一下前缀最大值(求前缀最大值是因为不确定最优选法是几个粒子,反正只要满足商店粒子的yshopy_{shop}yshop限制就行),对于每个商店粒子,用O(1)O(1)O(1)的时间复杂度查询。

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    void work()
    {
    /*
    n: 已有粒子个数
    m: 商店中的粒子个数
    x: 粒子能量
    y: 粒子反应性,最多共存粒子数量(除自己)
    vec1: vector<pair<val1:ll, val2:ll>>
    val1和val2分别对应已有粒子的能量和反应性
    vec2: vector<pair<val1:ll, val2:ll>>
    val1,val2分别记录商店粒子的能量和反应性
    now: 不考虑商店粒子时,实时维护的堆中所有粒子价值
    maxn: 不考虑商店粒子的最大价值
    pq: 小根堆,用于维护k大价值的粒子
    cmp: 比较函数,按粒子反应性升序排序
    p1: 一个指针,指向未进堆的最具反应性粒子
    f[i]: 解集大小不大于i,且只用不多于i-1个已有粒子,空出位置给商店粒子的最大价值
    */

    typedef long long ll;
    int n, m;
    ll x, y;
    ll now = 0, maxn = 0;
    priority_queue<ll, vector<ll>, greater<ll>> pq;
    auto cmp = [&](pair<ll, int> a, pair<ll, int> b) -> bool
    {
    return a.second < b.second;
    };

    // 读入
    scanf("%d%d", &n, &m);
    int p1 = n;
    vector<pair<ll, int>> vec1(n + 1), vec2(m + 1);
    vector<ll> f(n + 2); // 注意这里是n+2,因为加上商店粒子后最多有n+1个粒子,会用到n+1这个下标
    for (int i = 1; i <= n; i++)
    {
    scanf("%lld%lld", &x, &y);
    vec1[i] = make_pair(x, y);
    }
    sort(vec1.begin(), vec1.end(), cmp); // 按照反应性升序排序
    for (int i = 1; i <= m; i++)
    {
    scanf("%lld%lld", &x, &y);
    vec2[i] = make_pair(x, y);
    }

    // 第一次处理
    for (int k = n; k >= 1; k)
    {
    while (p1 >= 1 && vec1[p1].second + 1 >= k)
    { // 把反应性限制允许的粒子全部加入
    pq.push(vec1[p1].first);
    now += vec1[p1].first;
    p1;
    }
    while (pq.size() > k)
    { // 保证只留最大的k个粒子
    now -= pq.top();
    pq.pop();
    }
    maxn = max(maxn, now);
    }
    while (!pq.empty()) // 清空一下堆,为下一轮做准备
    pq.pop();
    p1 = n;
    now = 0;
    for (int k = n + 1; k >= 1; k)
    {
    while (p1 >= 1 && vec1[p1].second + 1 >= k)
    { // 和第一轮一样
    pq.push(vec1[p1].first);
    now += vec1[p1].first;
    p1;
    }
    while (pq.size() > k 1)
    { // 注意空出一个位置给商店粒子
    now -= pq.top();
    pq.pop();
    }
    f[k] = now;
    }
    for (int i = 2; i <= n + 1; i++)
    { // 处理一下前缀最大值,因为商店粒子(x,y)可以放到所有反应性限制不超过y的解集中
    f[i] = max(f[i], f[i 1]);
    }
    for (int i = 1; i <= m; i++)
    { // 输出答案,不一定用商店粒子,两者取较大即可
    printf("%lld ", max(f[vec2[i].second + 1] + vec2[i].first, maxn));
    }
    printf("\\n");
    return;
    }
    int main()
    {
    int T;
    scanf("%d", &T);
    while (T)
    {
    work();
    }
    return 0;
    }

    G. Operation Permutation

    文章太长,G、H分做另一篇文章讲解,详情见作者主页。

    H. Six Seven

    文章太长,G、H分做另一篇文章讲解,详情见作者主页。

    赞(0)
    未经允许不得转载:171主机测评 » CF2200 Codeforces Round 1084 (Div. 3) 题解 图解 超详细版
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址