【BZOJ1805】【IOI2007】Sail船帆 线段树优化贪心

2 篇文章 0 订阅
2 篇文章 0 订阅

       题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1805


       好久不写博客了,但是也不能老不写,对吧?于是我就随便找了这道题来水一篇。

       废话少说,这题有一个很明显的贪心策略,设每个位置上的旗子数量为 Si S i ,我们先按旗杆长度从小到大排个序,每次贪心地找到可行的位置中旗子数量最少的 k k Si全部加 1 1 ,最后Si×(Si1)÷2即为答案。时间复杂度 Θ(H) Θ ( ∑ H ) ,因为 H ∑ H 会达到 1010 10 10 ,所以显然是过不了的。

       要是使每次需要增加的 Si S i 的位置连续就好了,这样就可以直接线段树模拟了。仔细想想,这也一定是可行的,因为一定存在一种方案,使得最后的 Si S i 随着高度的增加而递减。但是怎样使 Si S i 保持这样的性质呢?我们可以这样考虑:假设 Si S i 现在有递减的性质,我们也已经知道了要修改的区间,设为 [l,r] [ l , r ] ,由于每次修改只增加 1 1 ,所以如果Sl1>Sl,直接修改不会影响递减性质,不用管;否则,也就是当 Sl1=Sl S l − 1 = S l 时,如果让 Sl S l 1 1 的话就会破坏性质,这时,我们可以找到与Sl相等的最左位置 Sl S l ′ 与最右位置 Sr S r ′ (注意, r r ′ 必须满足 rr r ′ ≤ r ),我们将 [l,r] [ l , r ] 的修改分为两块, [l,r] [ l , r ′ ] [r+1,r] [ r ′ + 1 , r ] ,如果将 [l,r] [ l , r ′ ] 的修改区间平移至 [l,l+rl] [ l ′ , l ′ + r ′ − l ] ,我们会发现这样既不改变性质又不影响答案,于是就可以直接上线段树了。时间复杂度 Θ(Nlog2H) Θ ( N l o g 2 H )

       贴下代码撒:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
namespace sgt
{
    int mx[200010],mn[200010],chg[200010],son[200010][2],root,cnt;
    void pushdown(int x)
    {
        mn[x]+=chg[x];
        mx[x]+=chg[x];
        if(son[x][0] && son[x][1])
        {
            chg[son[x][0]]+=chg[x];
            chg[son[x][1]]+=chg[x];
        }
        chg[x]=0;
    }
    void pushup(int x)
    {
        if(!son[x][0] || !son[x][1])return;
        mn[x]=mn[son[x][1]]+chg[son[x][1]];
        mx[x]=mx[son[x][0]]+chg[son[x][0]];
    }
    void build(int &x,int l,int r)
    {
        x=++cnt;
        mn[x]=mx[x]=chg[x]=0;
        if(l==r)return;
        int mid=(l+r)>>1;
        build(son[x][0],l,mid);
        build(son[x][1],mid+1,r);
        pushup(x);
    }
    void update(int a,int b,int k,int l,int r)
    {
        if(a>b || l>r)return;
        if(a==l && b==r)
        {
            chg[k]++;
            pushdown(k);
            return;
        }
        pushdown(k);
        int mid=(l+r)>>1;
        if(b<=mid)update(a,b,son[k][0],l,mid);
        else if(a>mid)update(a,b,son[k][1],mid+1,r);
        else
        {
            update(a,mid,son[k][0],l,mid);
            update(mid+1,b,son[k][1],mid+1,r);
        }
        pushup(k);
    }
    int ql(int x,int l,int r,int v)
    {
        pushdown(x);
        if(l==r)return l;
        int mid=(l+r)>>1;
        if(mn[son[x][0]]+chg[son[x][0]]>v)return ql(son[x][1],mid+1,r,v);
        else return ql(son[x][0],l,mid,v);
    }
    int qr(int x,int l,int r,int v)
    {
        pushdown(x);
        if(l==r)return l;
        int mid=(l+r)>>1;
        if(mx[son[x][1]]+chg[son[x][1]]<v)return qr(son[x][0],l,mid,v);
        else return qr(son[x][1],mid+1,r,v);
    }
    int query(int x,int l,int r,int v)
    {
        pushdown(x);
        if(l==r)return mn[x];
        int mid=(l+r)>>1;
        if(v<=mid)return query(son[x][0],l,mid,v);
        else return query(son[x][1],mid+1,r,v);
    }
    long long calc(int x,int l,int r)
    {
        pushdown(x);
        if(l==r)return 1LL*mn[x]*(mn[x]-1)/2LL;
        int mid=(l+r)>>1;
        return calc(son[x][0],l,mid)+calc(son[x][1],mid+1,r);
    }
}
int n;
pair<int,int>q[100010];
int h;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d%d",&q[i].first,&q[i].second),h=max(h,q[i].first);
    sort(q+1,q+n+1);
    sgt::build(sgt::root,1,h);
    for(int i=1;i<=n;i++)
    {
        int val=sgt::query(sgt::root,1,h,q[i].first-q[i].second+1);
        int l=sgt::ql(sgt::root,1,h,val),r=min(q[i].first,sgt::qr(sgt::root,1,h,val));
        sgt::update(l,l+r-(q[i].first-q[i].second+1),sgt::root,1,h);
        sgt::update(r+1,q[i].first,sgt::root,1,h);
    }
    printf("%lld",sgt::calc(sgt::root,1,h));
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值