zoukankan      html  css  js  c++  java
  • 【LOJ6620】「THUPC2019」不等式 / inequality(线段树)

    点此看题面

    大致题意: 给你两个长度为(n)的数组(a_i)(b_i),定义(f_k(x)=sum_{i=1}^k|a_ix+b_i|),对于(k=1sim n)的每个(f_k),求(f_k)的最小值。

    前言

    懒得写平衡树,于是就想了个线段树做法。

    还有,题目没有特殊说明不存在(a_i=0)的情况,但数据中确实不存在这样的情况,我的做法遇上这种情况可能要加一些特殊处理,然而我懒得写了

    前置知识

    先考虑一个简单的问题:

    (Problem 1)

    给你一个长度为(n)的数组(b_i),求(sum_{i=1}^n|x+b_i|)的最小值。

    显然根据初中数学可知,当(x)(b_i)中位数的相反数时,这个式子取最小值。

    再考虑一个升级版的问题:

    (Problem 2)

    给你两个长度为(n)的数组(a_i)(b_i),求(sum_{i=1}^n|a_ix+b_i|)的最小值。(假定任意(a_i>0)

    这看似复杂,实际上不难发现,(|a_ix+b_i|=a_i|x+frac{a_i}{b_i}|),也就是一个(|a_ix+b_i|)可以拆成(a_i)(|x+frac{a_i}{b_i}|)

    然后按上面(Problem 1)的方法做即可。

    虽说上面我们假定(a_i>0),但实际上对于(a_i<0)的情况,我们可以同时将(a_i)(b_i)变为其相反数,这样就能满足(a_i>0)了。

    前置处理

    我们可以发现,这道题要我们做的,其实就是每次加入一组(a_i)(b_i),维护(Problem 2)的答案。

    假如我们把(|a_ix+b_i|)拆成(a_i)(|x+frac{a_i}{b_i}|),由于(a_i<10^5),显然最后数的个数的规模是难以接受的。

    但如果我们能够开一个数组(p),以(frac{a_i}{b_i})为下标,那么我们实际上每次只要把(p_{frac{a_i}{b_i}})加上(a_i)就能很方便地维护数组。

    可是,以一个实数为下标显然是不可能的。因此,我们就需要在处理询问前先将(frac{a_i}{b_i})给离散化。

    注意,为了防止挂精度,推荐在排序比较分数大小时可以先交叉相乘再比较,避免出现除法。

    线段树

    设我们求出中位数的值是(e),且其在离散化后的值是(k),那么答案就是:

    [(e imessum_{i=1}^kp_i-sum_{i=1}^k p_i imes Fact_i)+(sum_{i=k}^n p_i imes Fact_i-p_k imessum_{i=k}^np_i) ]

    其中(Fact_i)表示(i)所表示的真实值。

    则我们需要一个数据结构,能够实现四种操作:单点修改、求中位数、求区间(sum p_i)和求区间(sum p_i imes Fact_i)

    于是我们就想到线段树。

    注意求区间(sum p_i imes Fact_i)看似棘手,但由于我们是单点修改,因此我们只要对于每个位置维护两个值即可。

    特殊地,根据先前定义我们可知,每次加入一组(a_i,b_i),设(t)(frac {b_i}{a_i})离散化后的值,那么我们会将(p_{t})加上(a_i),那么(p_t imes Fact_t)其实就是加上了(a_i imes Fact_t=a_i imes frac{b_i}{a_i}=b_i)

    而对于求中位数,其实我们如果维护(g=sum p_i),那么中位数就是第(lfloorfrac{g+1}2 floor)个数。

    然后,我们在线段树上二分,便可以找到中位数离散化后的值(k),进而就可以得到(e)了。

    至于具体实现,可见代码。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 500000
    #define LL long long
    #define DB double
    #define abs(x) ((x)<0?-(x):(x))
    using namespace std;
    int n,a[N+5],b[N+5],s[N+5],p[N+5];
    I bool cmp(CI x,CI y) {return 1LL*b[x]*a[y]<1LL*b[y]*a[x];}
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define tn (x<<3)+(x<<1)
    		#define D isdigit(c=tc())
    		int f;char c,*A,*B,FI[FS];
    	public:
    		I FastIO() {A=B=FI;}
    		Tp I void read(Ty& x) {x=0,f=1;W(!D) f=c^'-'?1:-1;W(x=tn+(c&15),D);x*=f;}
    }F;
    class SegmentTree//线段树
    {
    	private:
    		#define PT CI l=1,CI r=n,CI rt=1
    		#define LT l,mid,rt<<1
    		#define RT mid+1,r,rt<<1|1
    		#define PU(x) (T[x]=T[x<<1]+T[x<<1|1],S[x]=S[x<<1]+S[x<<1|1])
    		LL T[N<<2],S[N<<2];
    	public:
    		I void Upt(CI x,Con LL& y,Con LL& z,PT)//单点修改
    		{
    			if(l==r) return (void)(T[rt]+=y,S[rt]+=z);int mid=l+r>>1;
    			x<=mid?Upt(x,y,z,LT):Upt(x,y,z,RT),PU(rt);
    		}
    		I int Qmid(Con LL& rk,PT)//求中位数
    		{
    			if(l==r) return l;int mid=l+r>>1;
    			return rk<=T[rt<<1]?Qmid(rk,LT):Qmid(rk-T[rt<<1],RT);
    		}
    		I LL Qtot(CI x,CI y,PT)//区间求和1
    		{
    			if(x<=l&&r<=y) return T[rt];int mid=l+r>>1;
    			return (x<=mid?Qtot(x,y,LT):0)+(y>mid?Qtot(x,y,RT):0);
    		}
    		I LL Qsum(CI x,CI y,PT)//区间求和2
    		{
    			if(x<=l&&r<=y) return S[rt];int mid=l+r>>1;
    			return (x<=mid?Qsum(x,y,LT):0)+(y>mid?Qsum(x,y,RT):0);
    		}
    }S;
    int main()
    {
    	RI i,k;LL g=0;DB e,w;for(F.read(n),i=1;i<=n;++i) F.read(a[i]);for(i=1;i<=n;++i) F.read(b[i]);//读入
    	for(i=1;i<=n;++i) a[i]<0&&(a[i]=-a[i],b[i]=-b[i]),s[i]=i;sort(s+1,s+n+1,cmp);//方便起见,将a[i]取正
    	for(k=0,i=1;i<=n;++i) (!k||(1LL*b[s[k]]*a[s[i]])^(1LL*b[s[i]]*a[s[k]]))&&(s[++k]=s[i]),p[s[i]]=k;//离散化,排序后去重
    	for(i=1;i<=n;++i)
    	{
    		S.Upt(p[i],a[i],b[i]),k=S.Qmid((g+=a[i])+1>>1),e=1.0*b[s[k]]/a[s[k]];//线段树上查询中位数
    		w=(e*S.Qtot(1,k)-S.Qsum(1,k))+(S.Qsum(k,n)-e*S.Qtot(k,n)),printf("%.8lf
    ",w);//利用线段树查询所需信息计算答案
    	}return 0;
    }
    
  • 相关阅读:
    每个zone的low memory是怎么计算出来的
    /proc/meminfo中meminfo的计算方法
    shmem:
    tc:逼良为娼
    内核抢占
    html/css/javascript知识点集锦;完全小白开搞web编程
    netem设置了网卡的流量控制,为啥发包的延迟就搞不定呢?
    滑动窗口
    发送缓冲区sk_wmem_queued
    ASP.NET MVC 实现区域 项目分离 (比较好的方式)
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/LOJ6620.html
Copyright © 2011-2022 走看看