【万题详解】P1314 [NOIP2011 提高组] 聪明的质监员

课前C++恶搞小程序

今天,博主给大家带来了一个坑人小程序(鼠标乱飞),可以让你的好基友的鼠标瘫痪,然后……你就被她/他揍死了,言归正传,这个程序很简单,用死循环+随机位置就可以了!下面就是我的恶搞小程序。

献上代码^_^(无伤害):

//鼠标乱飞 按Alt+F4即可解决 
#include<bits/stdc++.h>
#include<windows.h>
using namespace std;
int main()
{
	
    while(1)
    {
        SetCursorPos(rand()%1000,rand()%1000);
    } 
}

注意是C++哦,别搞混成其他语言了。哦对了如果你想让鼠标不在乱飘的话请按下Alt+F4或Alt+Fn+F4这个按你们电脑的情况来看啦!!!

前言

抱歉,因为开学事情比较多,有一周没有更新了。

题目

题目描述

小T 是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有 n 个矿石,从 1 到 n 逐一编号,每个矿石都有自己的重量 wi​ 以及价值 vi​ 。检验矿产的流程是:

  1. 给定m 个区间 [li​,ri​];

  2. 选出一个参数 W;

  3. 对于一个区间 [li​,ri​],计算矿石在这个区间上的检验值 yi​:

yi​=j=li​∑ri​​[wj​≥W]×j=li​∑ri​​[wj​≥W]vj​

其中 j 为矿石编号。

这批矿产的检验结果 y 为各个区间的检验值之和。即:i=1∑m​yi​

若这批矿产的检验结果与所给标准值 s 相差太多,就需要再去检验另一批矿产。小T 不想费时间去检验另一批矿产,所以他想通过调整参数 W 的值,让检验结果尽可能的靠近标准值 s,即使得 ∣s−y∣ 最小。请你帮忙求出这个最小值。

输入格式

第一行包含三个整数 n,m,s,分别表示矿石的个数、区间的个数和标准值。

接下来的 n 行,每行两个整数,中间用空格隔开,第 i+1 行表示 i 号矿石的重量 wi​ 和价值 vi​。

接下来的 m 行,表示区间,每行两个整数,中间用空格隔开,第 i+n+1 行表示区间 [li​,ri​] 的两个端点 li​ 和ri​。注意:不同区间可能重合或相互重叠。

输出格式

一个整数,表示所求的最小值。

输入输出样例

输入 #1

5 3 15
1 5
2 5
3 5
4 5
5 5
1 5
2 4
3 3 

输出 #1

10

说明/提示

【输入输出样例说明】

当 W 选 44 的时候,三个区间上检验值分别为 20,5,020,5,0 ,这批矿产的检验结果为 2525,此时与标准值 S 相差最小为 1010。

【数据范围】

对于 10% 的数据,有 1≤n,m≤10;

对于 30%的数据,有 1≤n,m≤500 ;

对于 50% 的数据,有 1≤n,m≤5,000;

对于 70% 的数据,有 1≤n,m≤10,000 ;

对于 100%的数据,有 1≤n,m≤200,000,0<wi​,vi​≤106,0<s≤1012,1≤li​≤ri​≤n 。

解题思路

简述:

本题是一个比较明显的二分题,显然可以看出来这个标准值S是可以二分的,之后如果暴力O(nmlog1e6)就是50分。

但我们显然不用暴力,这里我们可以先预扫一遍数组,并用前缀和存w和v,之后再一个O(m)暴力判断就可以了。

和大于标准时加大l以加大mid,小于时减少r。

并一定要注意l和r的初始大小,l为所有值中最小-1,虽然貌似不减也可以过,r为最大值+1。

如果看懂了,就没必要看下面的了。

由于我们并不清楚要求的W的值,但是我们知道W的值不超过矿石中价值最大的,如果W大于了矿石中价值最大的,那么Y的值为0,无法达到最优解。

因此,很容易就能想到在确定W的值要用二分的方法。

在分析这道题的时候,我们很容易知道Y的值是满足单调性的,当W的值越大,Y的值越小,因为W越大,能够选的矿石就越少。

所以我们把得到的Y值作为判断条件,如果Y比S小,就说明检验值了,而W取大了。每次更改W的同时给ans取最小值。

那么Y又应该怎么求出呢?题目中n,m最大有2*10^5,

如果暴搜肯定超时,因此我们需要在枚举W的时候预处理。遍历w数组,保存满足条件的v和个数的前缀和。然后再遍历一遍要求的区间。

AC

#include <bits/stdc++.h>
using namespace std;
struct stone
{
    long long w; 
    long long v; 
};
bool compare(struct stone s1, struct stone s2)
{
    return s1.w < s2.w;
}
int main()
{
    long long n, m, s;
    cin >> n >> m >> s;
    struct stone rock[200001];
    struct stone rock_list[200001];
    for (int i = 1; i <= n; i++)
    {
        cin >> rock[i].w >> rock[i].v;
        rock_list[i].w = rock[i].w;
        rock_list[i].v = rock[i].v;
    }
    long *l = new long[m], *r = new long[m];
    for (int i = 0; i < m; i++)
    {
        cin >> l[i] >> r[i];
    }
    sort(rock_list + 1, rock_list + n, compare);
    long mini = 0x3f3f3f3f3f3f3f3f;
    int left = 0, right = n, mid = (right + left) / 2;
    while (left <= right)
    {
        struct stone *rock_temp = new struct stone[n + 1];
        long *num = new long[n + 1];
        num[0] = 0;
        rock_temp[0].v = 0;
        rock_temp[0].w = 0;
        for (int i = 1; i <= n; i++)
        {
            if (rock[i].w >= rock_list[mid].w + 1)
            {
                rock_temp[i].v = rock[i].v + rock_temp[i - 1].v;
                num[i] = num[i - 1] + 1;
            }
            else
            {
                rock_temp[i].v = rock_temp[i - 1].v;
                num[i] = num[i - 1];
            }
        }
        long long y = 0;
        for (int i = 0; i < m; i++)
        {
            long l1 = l[i];
            long r1 = r[i];
            y += (num[r1] - num[l1 - 1]) * (rock_temp[r1].v - rock_temp[l1 - 1].v);
        }
        if (llabs(y - s) < mini)
        {
            mini = llabs(y - s);
        }
        if (y < s)
        {
            right = mid - 1;
        }
        else if (y == s)
        {
            break;
        }
        else if (y > s)
        {
            left = mid + 1;
        }
        mid = (left + right) / 2;
    }
    cout << mini;
    return 0;
}

结尾

希望大家多多关注!!!

如果你能支持一下我,我十分感谢!!!

如果有人想在洛谷上做题,可以点下方链接:

https://www.luogu.com.cn/

如果你喜欢或想了解一下其他的算法,可以看看以下这些:

洛谷指南

洛谷使用指南_洛谷怎么看-CSDN博客

题目详解系列(部分):

【万题详解】洛谷P1282 多米诺骨牌-CSDN博客

【万题详解】洛谷P1238 走迷宫-CSDN博客

【万题详解】洛谷P1135奇怪的电梯-CSDN博客

【万题详解】洛谷P1510 精卫填海-CSDN博客

【万题详解】洛谷P1252 马拉松接力赛-CSDN博客

【万题详解】洛谷P1359 租用游艇-CSDN博客

【百题详解】洛谷P8508 做不完的作业-CSDN博客

【万题详解1】洛谷P1230 智力大冲浪-CSDN博客

【全网首发】洛谷贪心题解合集2-CSDN博客

【全网首发】洛谷贪心题解集合-CSDN博客

洛谷二分题集(3题)-CSDN博客

游戏系列:

C++棋类小游戏2-CSDN博客

C++自创棋类小游戏-CSDN博客

C++:史上最坑小游戏-CSDN博客

 C++:自创小游戏-CSDN博客

C++:下雪-CSDN博客

C++讲解系列(算法):

C++:第十五讲高精度算法-CSDN博客

C++:第十四讲动态规划初步-CSDN博客

C++:第十三讲BFS广度优先搜索-CSDN博客

C++:第十二讲DFS深搜(二)_c++匿名函数dfs-CSDN博客

 C++:第十一讲DFS深搜-CSDN博客

C++:第十讲二分查找-CSDN博客

前缀和与差分:

C++:第九讲前缀和与差分-CSDN博客

贪心:

C++:第八讲贪心算法1-CSDN博客

C++讲解系列(基础入门):

排序:

C++:第七讲冒泡排序-CSDN博客

函数:

C++第6讲max和min函数_c++ min函数-CSDN博客

C++第五讲函数初步-CSDN博客

for循环&数组:

C++第四讲for循环及数组-CSDN博客

if语句&else语句及运算:

C++第三讲:C++中的逻辑运算符及if else语句-CSDN博客

基础:

C++第二讲输入与输出-CSDN博客

C++第一讲认识C++编译器-CSDN博客

欢迎收看,希望大家能三连!

最后认识一下,我是爱编程的喷火龙廖,我们有缘再见!

  • 15
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 29
    评论
评论 29
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喷火龙廖

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值