bzoj3110: [Zjoi2013]K大数查询

链接

  http://www.lydsy.com/JudgeOnline/problem.php?id=3110

题解

  惊!我突然明白 SDOI2017Day2T3 我为什么怒丢30分了,这道题里刚开始犯了同样的错误,虽然我正确处理了区间赋值和区间加的先后,但是我外面在打赋值标记的时候并没有将加标记清零。
  这题啊,就是整体二分啊。
  你就二分一个答案 mid ,然后顺着扫一遍,把 cmid 的操作在线段树的对应区间上加 1 <script type="math/tex" id="MathJax-Element-195">1</script>。每个查询看看,如果加多了就说明答案需要更大,否则答案需要更小。分治下去就行了。

代码

//整体二分+线段树
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cmath>
#define maxn 50005
#define ll long long
using namespace std;
ll N, M, ndtot, ans[maxn];
struct opt{ll type, a, b, c, id;}o[maxn];
vector<opt> v;
struct segtree
{
    segtree *ch[2];
    ll l, r, sum, set, add, ans;
}pool[maxn<<2], *root;
void pushdown(segtree *p)
{
    if(p->set)
    {
        p->sum=0;
        if(p->ch[0])
            p->ch[0]->set=p->ch[1]->set=1,
            p->ch[0]->add=p->ch[1]->add=0;
        p->set=0;
    }
    if(p->add)
    {
        p->sum+=(p->r-p->l+1)*p->add;
        if(p->ch[0])p->ch[0]->add+=p->add, p->ch[1]->add+=p->add;
        p->add=0;
    }
}
void pushup(segtree *p)
{
    if(!p->ch[0])return;
    pushdown(p->ch[0]), pushdown(p->ch[1]);
    p->sum=p->ch[0]->sum+p->ch[1]->sum;
}
void segadd(segtree *p, ll l, ll r)
{
    pushdown(p);
    ll mid=p->l+p->r>>1;
    if(l<=p->l and r>=p->r){p->add++;return;}
    if(l<=mid)segadd(p->ch[0],l,r);
    if(r>mid)segadd(p->ch[1],l,r);
    pushup(p);
}
ll segsum(segtree *p, ll l, ll r)
{
    pushdown(p);
    int ans=0, mid=p->l+p->r>>1;
    if(l<=p->l and r>=p->r)return p->sum;
    if(l<=mid)ans+=segsum(p->ch[0],l,r);
    if(r>mid)ans+=segsum(p->ch[1],l,r);
    return ans;
}
void build(segtree *p, ll l, ll r)
{
    ll mid=l+r>>1;
    p->l=l, p->r=r;
    if(l==r)return;
    build(p->ch[0]=pool+ ++ndtot,l,mid);
    build(p->ch[1]=pool+ ++ndtot,mid+1,r);
}
void solve(ll l, ll r, vector<opt> &lis)
{
    ll mid=ceil((l+r)/2.0), i, tmp;
    vector<opt> lis1, lis2;
    vector<opt>::iterator it;
    if(l==r)
    {
        for(it=lis.begin();it!=lis.end();it++)ans[it->id]=mid;
        return;
    }
    root->set=1;
    root->add=0;
    for(it=lis.begin();it!=lis.end();it++)
    {
        if(it->type==1 and it->c>=mid)
            segadd(root,it->a,it->b), lis2.push_back(*it);
        if(it->type==1 and it->c<mid)lis1.push_back(*it);
        if(it->type==2)
        {
            tmp=segsum(root,it->a,it->b);
            if(tmp>=it->c)lis2.push_back(*it);
            else it->c-=tmp, lis1.push_back(*it);
        }
    }
    solve(l,mid-1,lis1);
    solve(mid,r,lis2);
}
ll read(ll x=0)
{
    char c=getchar(); bool f=0;
    while(c<48 or c>57)f=f or c=='-', c=getchar();
    while(c>=48 and c<=57)x=(x<<1)+(x<<3)+c-48, c=getchar();
    return f?-x:x;
}
void init()
{
    ll a, b, c, i;
    N=read(), M=read();
    for(i=1;i<=M;i++)o[i].type=read(), o[i].a=read(), o[i].b=read(), o[i].c=read(),
                     o[i].id=i;
    build(root=pool+ ++ndtot,1,N);
    for(i=1;i<=M;i++)
    {
        if(o[i].type==1)segadd(root,o[i].a,o[i].b);
        else o[i].c=min(o[i].c,segsum(root,o[i].a,o[i].b));
    }
    for(i=1;i<=M;i++)v.push_back(o[i]);
}
int main()
{
    init();
    solve(-N,N,v);
    for(int i=1;i<=M;i++)if(o[i].type==2)printf("%lld\n",ans[i]);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值