BZOJ 3110 题解

题目大意

有N个位置,M个操作。操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c
如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少。

我的做法

整体二分+线段数

二分答案,然后%¥#*&#¥

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
struct tree
{
    ll sum;
    int l,r;
    tree *ls,*rs;
    ll t1,t2;
    tree()
    {
        sum=t2=0;
        t1=-1;
        l=r=0;
        ls=rs=0;
    }
};
tree *top;
void build(tree *&p,int l,int r)
{
    p=new tree;
    p->l=l;
    p->r=r;
    if(l==r)
     return;
    int mid=(l+r)>>1;
    build(p->ls,l,mid);
    build(p->rs,mid+1,r);
}
void addtag(tree *&p,ll t1,ll t2)
{
    if(t1!=-1)
    {
        p->t1=t1;
        p->t2=0;
        p->sum=t1*(p->r-p->l+1);
    }
    p->t2+=t2;
    p->sum+=t2*(p->r-p->l+1);
}
void downtag(tree *&p)
{
    if((p->t1!=-1||p->t2)&&p->l!=p->r)
    {
         addtag(p->ls,p->t1,p->t2);
         addtag(p->rs,p->t1,p->t2);
    }
    p->t1=-1;
    p->t2=0;
}
void maintain(tree *&p)
{
    if(p->l==p->r)
     return;
    p->sum=p->ls->sum+p->rs->sum;
}
void add(tree *&p,int l,int r,ll t1,ll t2)
{
    if(l<=p->l&&r>=p->r)
    {
        addtag(p,t1,t2);
        return;
    }
    downtag(p);
    int mid=(p->l+p->r)>>1;
    if(l<=mid)
     add(p->ls,l,r,t1,t2);
    if(r>mid)
     add(p->rs,l,r,t1,t2);
    maintain(p);
}
ll query(tree *&p,int l,int r)
{
    if(l<=p->l&&r>=p->r)
     return p->sum;
    ll s=0;
    downtag(p);
    int mid=(p->l+p->r)>>1;
    if(l<=mid)
     s+=query(p->ls,l,r);
    if(r>mid)
     s+=query(p->rs,l,r);
    return s;
}
struct p
{
    int id;
    int op,a,b;
    ll c;
    p()
    {
        id=op=a=b=c=0;
    }
};
p a[100010],b[100010],c[100010];
int ans[100010];
int d[100010];
int n,m;
void solve(int l,int r,ll sl,ll sr)
{
    if(l>r)
     return;
    if(sl==sr)
    {
        int i;
        for(i=l;i<=r;i++)
         ans[a[i].id]=sl;
        return;
    }
    int l1=0,l2=0;
    int sm=(sl+sr)>>1;
    int i;
    for(i=l;i<=r;i++)
     if(a[i].op==1)
      if(a[i].c>sm)
      {
        add(top,a[i].a,a[i].b,-1,1);
        c[++l2]=a[i];
      }
      else
       b[++l1]=a[i];
     else
     {
        ll s=query(top,a[i].a,a[i].b);
        if(a[i].c>s)
        {
            a[i].c-=s;
            b[++l1]=a[i];
        }
        else
         c[++l2]=a[i];
     }
    add(top,1,n,0,0);
    int j;
    for(i=l,j=1;j<=l1;i++,j++)
     a[i]=b[j];
    for(j=1;j<=l2;i++,j++)
     a[i]=c[j];
    solve(l,l+l1-1,sl,sm);
    solve(l+l1,r,sm+1,sr);
}
int main()
{
    int j;
    scanf("%d%d",&n,&m);
    build(top,1,n);
    for(j=1;j<=m;j++)
    {
        scanf("%d%d%d%lld",&a[j].op,&a[j].a,&a[j].b,&a[j].c);
        a[j].id=j;
        d[j]=a[j].op;
    }
    solve(1,m,1,n);
    for(j=1;j<=m;j++)
     if(d[j]==2)
      printf("%d\n",ans[j]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值