首页 > 其他分享 >P2672 NOIP2015 普及组 推销员

P2672 NOIP2015 普及组 推销员

时间:2024-10-20 08:48:39浏览次数:7  
标签:P2672 last 疲劳 int 所剩 maxv NOIP2015 推销员 id

P2672 [NOIP2015 普及组] 推销员 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

我还是相信,大部分人是想不出贪心的。

时间复杂度 \(O(n\log n)\) 但是常数极大,运用线段树,这题数据过水,甚至我一个写错了的线段树都能拿满(除了#3hack)。非贪心。

首先按距离大小分类,并在每一类里进行排序,这里使用vector实现,为了方便弹出我们从小到大排,这样我们只需要统计每一类的末尾值就能统计出最大值。

对于选 i 个人,就相当于我先选 i - 1 个人,再选一个人,我们要求选 1 ~ n 个人的结果,就可以利用上一个的结果,只需要选出现在最大的这个人即可。

因为有路程的问题,这我们也算在疲劳值中,那在选完一个人 u 之后就会出现几种情况,设 r 为 u 所在的距离一类,last 为当前最大距离的类;

  1. 如果所剩距离疲劳值 \(last \ge r\) ,那么选了 r 将不影响任何类的路程疲劳值;
  2. 如果所剩距离疲劳值 \(r > last\),那么对于已经被处理过路程疲劳的类即 1~last,不用再处理,对于 last + 1 ~ r - 1 则直接把所剩路程疲劳值清零,对于 r ~ n 我们直接减去当前 r 所剩的距离疲劳值。
    每次询问句询问区间最大值,保存这个最大值的来源 r,然后让这个类 r,弹出最后一个,让新的加入区间,最大值加上之前的 sum,就是当前答案。

按上面进行,需要实现区间覆盖,区间加,求区间最值。实际上区间覆盖可以用区间加代替。

而实际上我实现思路不同,我们假设第一个选了 r,如果把所有数距离疲劳都减去 \(S_r\),那么对于 1 ~ r - 1 还需要加上 \(S[r] - S[j]\),因为它没有那么多距离。如果只看相对大小的话,那么就相当于 1~r - 1 加上了 \(S[r] - S[j]\),照这个思路很快就能想出上面的分类。而有所不同:

  1. 如果所剩距离疲劳值 \(last \ge r\) ,那么选了 r 将不影响任何类的路程疲劳值;
  2. 如果所剩距离疲劳值 \(r > last\),对于已经被处理过路程疲劳的类即 1~last,因为不需要继续减,就相当于加上 \(S[r]\)。对于 last + 1 ~ r - 1 则相当于加上 \(S[r]- S[j]\),上面有解释。我们只看相对大小,而为了方便直接算出答案,还是把实际影响加进去即:全部减去 \(S[r]\)。
    对于每一类新加入值,就相当于把线段树对应值减去当前最大maxv(即这个点的值),再加上新点值即可,别忘了 pop_back()。

按照上面进行线段树,即可。

感觉还是写麻烦了。


#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <queue>

using namespace std;

const int N = 100010, M = N * 8;

int n, m, cnt;
vector<int> s[N];
int id[N];
int f[N];
int S[N];

struct Nodes
{
    int l, r;
    int id, maxv;
    int add;
}tr[M];

struct Node 
{
    int s, a;
    bool operator<(const Node &W)const
    {
        return s < W.s;
    }
}g[N];

void print(int A)
{
    Nodes u = tr[A];
    printf("%d %d %d %d %d %d %d\n", A, u.l, u.r, u.id, u.maxv, u.add);
    puts("");
}

void pushup(Nodes &u, Nodes &l, Nodes &r)
{
    u.l = l.l;
    u.r = r.r;
    if (l.maxv > r.maxv) 
    {
        u.maxv = l.maxv;
        u.id = l.id;
    }
    else
    {
        u.maxv = r.maxv;
        u.id = r.id;
    }
}

void pushup(int u)
{
    pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
}

void pushdown(Nodes &u, Nodes &l, Nodes &r)
{
    l.add += u.add;
    r.add += u.add;
    l.maxv += u.add;
    r.maxv += u.add;
    u.add = 0;
}

void pushdown(int u)
{
    pushdown(tr[u], tr[u << 1], tr[u << 1 | 1]);
}

void build(int u, int l, int r)
{
    if (l == r) tr[u] = {l, r, l, s[l].back() + S[l], 0};
    else 
    {
        tr[u] = {l, r};
        int mid = l + r >> 1;
        build(u << 1, l, mid);
        build(u << 1 | 1, mid + 1, r);
        pushup(u);
    }
    // print(u);
}

void modify(int u, int l, int r, int add)
{
    if (l <= tr[u].l && tr[u].r <= r) 
    {
        tr[u].add += add, tr[u].maxv += add;
    }
    else 
    {
        pushdown(u);
        int mid = tr[u].l + tr[u].r >> 1;
        if (l <= mid) modify(u << 1, l, r, add);
        if (r > mid) modify(u << 1 | 1, l, r, add);
        pushup(u);
    }
    
}


Nodes query(int u, int l, int r)
{
    if (l <= tr[u].l && tr[u].r <= r) return tr[u];
    else 
    {
        pushdown(u);
        int mid = tr[u].l + tr[u].r >> 1;
        if (r <= mid) return query(u << 1, l, r);
        else if (l > mid) return query(u << 1 | 1, l, r);
        else 
        {
            Nodes A, B, res;
            A = query(u << 1, l, r);
            B = query(u << 1 | 1, l, r);
            pushup(res, A, B);
            return res;
        }
    }
}


int main()
{
    cin >> n;
    for (int i = 1; i <= n; i ++ ) scanf("%d", &g[i].s);
    for (int i = 1; i <= n; i ++ ) scanf("%d", &g[i].a);
    
    int last = -1;
    for (int i = 1; i <= n; i ++ ) 
    {
        int a = g[i].a;
        if (last != g[i].s) 
        {
            cnt ++ ;
            last = g[i].s;
            S[cnt] = 2 * g[i].s;
        }
        s[cnt].push_back(a);
    }
    for (int j = 1; j <= cnt; j ++ ) sort(s[j].begin(), s[j].end());
    
    build(1, 1, cnt);
    
    last = 0;
    int sum = 0;
    for (int i = 1; i <= n; i ++ )
    {
        auto t = query(1, 1, cnt);
        int r = t.id;
        // cout << last << ' ' << query(1, 4, 4).maxv << endl;
        
        if (last < r)
        {
            for (int j = last + 1; j < r; j ++ )
            {
                modify(1, j, j, S[r] - S[j]);
            }
            
            if (last) modify(1, 1, last, S[r]);
            modify(1, 1, cnt, -S[r]);
            //实际上这两句可以合并为
            //modify(1, last + 1, cnt, -S[r]);
        }
        last = max(last, r);
        
        modify(1, r, r, -s[r].back()); 
        s[r].pop_back();
        if (s[r].size()) modify(1, r, r, s[r].back());
        else modify(1, r, r, -0x3f3f3f3f);
        sum += t.maxv;
        
        cout << sum << '\n';
    }
    return 0;
    
}

标签:P2672,last,疲劳,int,所剩,maxv,NOIP2015,推销员,id
From: https://www.cnblogs.com/blind5883/p/18486913

相关文章

  • NOIP 2015 推销员
    描述阿明是一名推销员,他奉命到螺丝街推销他们公司的产品。螺丝街是一条死胡同,出口与入口是同一个,街道的一侧是围墙,另一侧是住户。螺丝街一共有N家住户,第i家住户到入口的距离为Si​米。由于同一栋房子里可以有多家住户,所以可能有多家住户与入口的距离相等。阿明会从入口进入,依......
  • [Ynoi2012] NOIP2015 充满了希望
    [Ynoi2012]NOIP2015充满了希望题意给一个长为\(n\)的序列,有\(m\)个操作,操作编号从\(1\)到\(m\),每个操作为:1xy:将序列位置为\(x,y\)的两个元素交换。2lrx:将序列区间\([l,r]\)内所有元素修改为\(x\)。3x:查询序列\(x\)位置的值。现在有\(q\)次查询,每次......
  • 【贪心】【二分】[NOIP2015]跳石头
    https://ac.nowcoder.com/acm/contest/22353/C正确的思路是二分查找+贪心。具体来说,可以通过二分来猜测一个最小的跳跃距离,然后通过贪心算法判断是否可以通过移除石头使所有的跳跃都满足这个最小跳跃距离。这种方法的核心是逐步逼近最优解,而不是直接删除前m小的元素。细节:在......
  • [NOIP2015 提高组] 子串
    算法状态定义最初显然可以想到\(f[i][j][k]\)表示\(A\)串前\(i\)个,\(B\)串前\(j\)个,分割了\(k\)个子串但是这样无法递推\(k\)维于是加上一位\(f[i][j][k][0/1]\),最后一维表示是否选择\(A\)子串当前这一位,也就可以递推的计算状态转移当前位置不使......
  • 信息学奥赛初赛天天练-81-NOIP2015普及组-完善程序-二分答案、二分查找、中位数、二分
    1完善程序(单选题,每小题3分,共30分)中位数median给定n(n为奇数且小于1000)个整数,整数的范围在0∼m(0<m<2^31)之间,请使用二分法求这n个整数的中位数。所谓中位数,是指将这n个数排序之后,排在正中间的数。(第五空2分,其余3分)01#include<iostream>02usingnamespace......
  • 信息学奥赛初赛天天练-80-NOIP2015普及组-基础题5-错位排列、二叉树、完全二叉树、叶
    NOIP2015普及组基础题521重新排列1234使得每一个数字都不在原来的位置上,一共有()种排法22一棵结点数为2015的二叉树最多有()个叶子结点2相关知识点1)错位排列考虑一个有n个元素的排列,若一个排列中所有的元素都不在自己原来的位置上,那么这样的排列就......
  • 洛谷 P2680 [NOIP2015 提高组] 运输计划
    洛谷P2680[NOIP2015提高组]运输计划题意给出一棵树和\(m\)条路径,可以选择一条边,把边权改为\(0\),求\(m\)条路经长度最大值的最小值。思路看到最大值最小,可以想到二分答案,答案具有单调性。考虑如何判定答案\(x\)是否可行。统计所有长度大于\(x\)的路径,统计它们共......
  • 信息学奥赛初赛天天练-78-NOIP2015普及组-基础题3-中断、计算机病毒、文件传输协议FTP
    NOIP2015普及组基础题38所谓的“中断”是指()A操作系统随意停止一个程序的运行B当出现需要时,CPU暂时停止当前程序的执行转而执行处理新情况的过程C因停机而停止一个程序的运行D电脑死机9计算机病毒是()A通过计算机传播的危害人体健康的一种......
  • 信息学奥赛初赛天天练-77-NOIP2015普及组-基础题2-二进制、连通图、最小生成树、链表
    NOIP2015普及组基础题24在计算机内部用来传送、存贮、加工处理的数据或指令都是以()形式进行的A二进制码B八进制码C十进制码D智能拼音码5下列说法正确的是()ACPU的主要任务是执行数据运算和程序控制B存储器具有记忆能力,其中信息任何时候都不会......
  • 信息学奥赛初赛天天练-76-NOIP2015普及组-基础题1-计算机存储、硬件系统、操作系统、
    NOIP2016普及组基础题111MB等于()A10000字节B1024字节C1000×1000字节D1024×1024字节2在PC机中,PENTIUM(奔腾)、酷睿、赛扬等是指()A生产厂家名称B硬盘的型号CCPU的型号D显示器的型号3操作系统的作用是()A把源程序译成目......