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

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


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

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

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

猜你喜欢

转载自blog.csdn.net/qq_42112677/article/details/80713642