• P1314 聪明的质监员[二分答案]


    题目描述

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

    1 、给定mm个区间[L_i,R_i][L**i,R**i];

    2 、选出一个参数WW

    3 、对于一个区间[L_i,R_i][L**i,R**i],计算矿石在这个区间上的检验值Y_iY**i

    img

    这批矿产的检验结果YY 为各个区间的检验值之和。即:Y_1+Y_2...+Y_mY1+Y2...+Y**m

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

    解析

    看到区间操作,首先想到可能数据结构,但是我一看。数据范围吓人,如果你一定要用的话,祝您线段树过百万。

    分析题目,我们需要求一个限制条件的最小值,很容易想到二分答案。看到题述公式,经验告诉我们这个式子可以分开求,也就是每次check时求一组满足(w_j>W)(m)个询问的(sum_j1)(sum_jv_j),然后乘起来就得到答案。但是(O(nm))的求肯定会爆掉,我们就自然而然地考虑一个前缀和的优化,减少大量计算。

    参考代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<string>
    #include<cstdlib>
    #include<queue>
    #include<vector>
    #define INF 999999999999
    #define PI acos(-1.0)
    #define N 200010
    #define MOD 2520
    #define E 1e-12
    #define ll long long
    using namespace std;
    inline ll read()
    {
    	ll f=1,x=0;char c=getchar();
    	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    	return x*f;
    }
    inline ll max(ll x,ll y){return x>y?x:y;}
    inline ll min(ll x,ll y){return x<y?x:y;}
    ll n,m,S;
    int l[N],r[N],w[N],v[N];
    ll tmp1[N],tmp2[N];
    ll res;
    inline bool check(int x)
    {
    	res=0;
    	for(int i=1;i<=n;++i){
    		tmp1[i]=tmp2[i]=0;
    		if(w[i]>=x) tmp1[i]=tmp1[i-1]+1,tmp2[i]=tmp2[i-1]+v[i];
    		else tmp1[i]=tmp1[i-1],tmp2[i]=tmp2[i-1];
    	}
    	for(int i=1;i<=m;++i)
    		res+=(tmp1[r[i]]-tmp1[l[i]-1])*(tmp2[r[i]]-tmp2[l[i]-1]);
    	if(res-S==0){printf("0
    ");exit(0);}
    	if(res-S>0) return 1;
    	else return 0;
    }
    int main()
    {
    	n=read(),m=read(),S=read(); 
    	ll maxx=-1,minn=INF;
    	for(int i=1;i<=n;++i){
    		w[i]=read(),v[i]=read();
    		minn=min(minn,w[i]),maxx=max(maxx,w[i]);
    	}
    	for(int i=1;i<=m;++i) l[i]=read(),r[i]=read();
    	ll l=minn-1,r=maxx+1;
    	ll ans=0x3f3f3f3f3f3f3f3f;
    	while(l<r){//据说只有10%的程序员写的对二分(摊 
    		int mid=(l+r+1)>>1;
    		if(check(mid)) l=mid;
    		else r=mid-1;
    		res=llabs(res-S);//注意longlong类型的绝对值函数要用这个,否则会溢出 
    		ans=min(ans,res);//找最优解 
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    明年,我们依然年轻
    总有些东西会如台风一样的来
    ora00257错误处理方法
    ORACLE登录错误的解决方法
    C#中public new void add()的new在这里的意义
    Oracle Form Builder配置问题的一些总结
    作为程序员,你应该知道的职场晋升之路
    ORA01034错误的解决方法
    【转】JQUERY刷新页面
    【转】对C# 中堆栈,堆,值类型,引用类型的理解
  • 原文地址:https://www.cnblogs.com/DarkValkyrie/p/11599616.html
Copyright © 2020-2023  润新知