查税( 斜率优化&单调队列维护凸包 &分块 )

id3167
有n个办公室,m个操作,依次读入
type
如果type为1 , 接着读入 T K Z S , 表示一个公司于T时刻进驻K办公室,每天盈利为Z,其一开始有S元。若K位置本有别的公司,别的公司会被覆盖。
如果type为2 , 接着读入T A B 表示于T时刻,你要找出区间[A,B]内,最有钱的公司的钱数。(关于钱的值都可以为负)
若AB内无公司输出“nema”

每次操作T不相同。

显然,题意是要插入很多条直线y=kx+b,在线询问当x为某值是,区间上的直线们的最大值。
如果写暴力的话,查询O(N),插入O(1),考虑分块平均平均调剂调剂,把它们都搞成O( N ) 的 。
我们把直线的参数k,b看做是二维平面BOK上的点。每个块互不影响地插入块上的直线 。

我们可以发现本题有个单调性,若某两条直线 y=k1x+b1 , y=k2x+b2 。设 k1<k2 , 如果你 b1 也还小于 b2 l1 就根本没有保留的价值了。
插入的话,删除原来在那个位置的直线,重构整个块(好暴力啊)
以k为关键字升序排序块内元素。构造一个单调队列,从队尾加元素,其实根据上述单调性,我们发现是在一个二维平面上维护一个上凸壳。叉积维护凸性即可。
对于询问,若一个块没有被询问区间包含,暴力地扫出答案。其余被包含的块可以维护单调队列队首元素,保证队首最优(判断条件即 klx+bl < kl+1x+bl+1 -> ++l)
问题得以解决

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>

using namespace std ;

#define N 100010
#define SRN 351

typedef long long ll ;

int n , m , i , j , k , T ;

const int inf = 210000000 ;

struct point {
    ll k , b , ps ;
}a[N] ;

ll operator *( point a , point b ) {
    return a.k * b.b - b.k * a.b ;
}

point operator -( point a , point b ) {
    point ret ;
    ret.ps = 0 , ret.k = a.k-b.k , ret.b = a.b-b.b ;
    return ret ;
}

struct Block {
    int q[SRN] ; // According to Sorted Postition
    int st , en , l , r ;
}B[SRN] ;

point tmp[N] ;

struct FOR_BRUTE {
    ll k , b  ;
}Y[N] ;

ll min( ll a , ll b ) {
    return a < b ? a : b ;
}

ll max( ll a , ll b ) {
    return a > b ? a : b ;
}

int main() { 
    scanf("%d%d",&n,&m ) ;
    int sqt = sqrt( n ) ;
    int l , r ;
    for( i=1 , l = 1 , r =sqt ; l<=n ; l = r + 1 , r = ( r+sqt>n ? n : r+sqt ) , i++ ) {
        B[i].st = l , B[i].en = r ;
        B[i].l = 1 , B[i].r = 0 ;
        T = i ;
    }
    for( i=1 ; i<=n ; i++ ) a[i].ps = i , a[i].k = inf , Y[i].k = inf ;
    while( m-- ) {
        int typ ;
        scanf("%d",&typ ) ;
        if( typ==1 ) {
            ll x ,  ps , s ,  k , b ;
            scanf("%lld%lld%lld%lld",&x,&ps,&k,&s ) ;
            b = -k * x + s ;
            Y[ps].k = k , Y[ps].b = b ;
            int l ; bool proce = 0 ;
            for( i=1 ; i<=T ; i++ ) if( B[i].st<=ps && B[i].en>=ps ) break ;
            for( j=B[i].st , l = B[i].st ; l<=B[i].en ; l++ ) {
                if( a[j].ps==ps ) j++ ;
                if( proce==0 && ( k<a[j].k || j>B[i].en ) ) {
                    tmp[l].k = k , tmp[l].b = b ;
                    tmp[l].ps = ps ;
                    proce = 1 ;
                } else tmp[l] = a[j++] ;
            }
            for( j=B[i].st ; j<=B[i].en ; j++ ) a[j] = tmp[j] ;
            int r = 1 ;
            l = 1 ;
            B[i].q[1] = B[i].st ;
            B[i].q[2] = B[i].st + 1 ;
            if( a[ B[i].q[2] ].k!=inf && B[i].st + 1 <= B[i].en ) {
                r = 2 ;
                for( j=B[i].st+2 ; a[j].k!=inf && j<=B[i].en ; j++ ) {
                    while( l<=r-1 && ( a[j]-a[ B[i].q[r-1] ]  ) * ( a[ B[i].q[r] ] - a[ B[i].q[r-1] ] ) < 0  ) r-- ;
                    B[i].q[++r] = j ;
                }
            }
            B[i].l = l , B[i].r = r  ;

        } else {
            ll x , aa  , b , ans=-100000000000000ll ;
            scanf("%lld%lld%lld",&x,&aa,&b ) ;
            if( b<aa ) swap( aa , b ) ;
            for( i=1 ; i<=T ; i++ ) {
                if( b<B[i].st || aa>B[i].en ) continue ;
                // u->v 
                if( aa<=B[i].st && b>=B[i].en ) {
                    int l = B[i].l , r = B[i].r ;
                    if( l>r ) continue ;
                    while( l<=r-1 && a[ B[i].q[l] ].k * x + a[ B[i].q[l] ].b < a[ B[i].q[l+1] ].k * x + a[ B[i].q[l+1] ].b  ) l++ ;
                    ans = max( a[ B[i].q[l] ].k * x + a[ B[i].q[l] ].b , ans ) ;
                    B[i].l = l , B[i].r = r  ;
                } else {
                    int u = max( B[i].st ,aa ) , v = min( B[i].en , b ) ;
                    for( j=u ; j<=v ; j++ ) if( Y[j].k!=inf ) ans = max( Y[j].k * x + Y[j].b , ans ) ;
                }
            }
            if( ans==-100000000000000ll ) puts("nema" ) ; else printf("%lld\n",ans ) ;
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值