洛谷P4344 脑洞治疗仪 [SHOI2015] 线段树+二分/珂朵莉树(未完成QAQ)

正解:线段树+二分/珂朵莉树

解题报告:

先放个传送门qwq(啊发现好多题解都忘了放传送门呢QAQ有时间一起补上QAQ

然后大概说下思路,其实比较好想只是实现有点儿复杂呢?

大概就是个线段树+二分鸭

首先建棵线段树,存这段区间内所有1的个数

港最简单的操作,就是那个0lr.相当于就lr都变成0,没什么可说的嗷,基本操作qwq

然后1和2的操作想法都是类似的,分别港下趴还是qwq

首先1l0r0l1r1,肯定可以直接赋值0了不解释

首先如果l0r0中1的数量是大于​l0r0中0的数量就直接让l1r1赋值为1就成了不需要别的操作了,依然基本操作呢

但是如果小于,思考怎么做

肯定是从前往后跑能补就补就是了,那怎么看能不能补呢,首先我们肯定要先定位到第一个是0的点

这个可以用query直接求下判断是否是1,然后如果是1就二分查找找到第一个是0的点(关于二分的这个之后会另外讲实现的qwq)

然后继续用二分找到第一个不是0的点,那么中间这一段就都是0

然后判断,如果这一段的0的长度大于可以补的长度了,直接把能补的长度那段赋值成1,然后就补不上了退出

然后如果小于等于,就可以继续补,就把这一段赋值成1继续补重复之间的步骤就成

然后操作1就讲完了,还是比较好理解的呢?

然后是2lr,这个一样是用的二分(听说,还有种神仙方法线段树存的是最长0序列长度?这个怎么操作?mk下会学习落实的qwq)

差不多套路,首先判断是不是1如果是1让它跳到0去

然后二分查找找出这一段0的长度,然后ans取长度max就成

最后说下这个二分,就可以结束啦!

首先明确mid指这一段中连续的0/1(函数中用k表示)的长度

然后query查找,查这一段的和是多少,如果和=k*这段长度,说明这是一段连续长度,说明能继续延伸,就l=mid+1

否则不能的话就r=mid-1咯

然后就完美结束啦?那这道题就还有俩坑没填,一个是线段树存最长做法,一个是珂朵莉做法

没啦!

 

#include<bits/stdc++.h>
using namespace std;
#define ll int
#define rp(i,x,y) for(register ll i=x;i<=y;++i)
#define lc(x) x<<1
#define rc(x) (x<<1)|1
#define md(x,y) (x+y)>>1

const ll N=200000+10;
ll n,m,tr[N<<2],add[N<<2],trtr[N<<2];

inline ll read()
{
    char ch=getchar();ll x=0;bool y=1;
    while(ch!='-' && (ch<'0' || ch>'9'))ch=getchar();
    if(ch=='-')y=0,ch=getchar();
    while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=getchar();
    return y?x:-x;
}
inline void pushdown(ll d,ll l,ll r,ll mid)
{
    if(add[d]!=0)
    {
        --add[d];
        tr[lc(d)]=add[d]*(mid-l+1);tr[rc(d)]=add[d]*(r-mid);
        add[lc(d)]=add[d]+1;add[rc(d)]=add[d]+1;
        add[d]=0;
    }
}
void update(ll d,ll l,ll r,ll x,ll y,ll k)
{
    if(l>y || x>r)return;
    if(l>=x && r<=y){tr[d]=(r-l+1)*k;add[d]=k+1;return;}
    ll mid=md(l,r);pushdown(d,l,r,mid);
    if(x<=mid)update(lc(d),l,mid,x,y,k); if(mid<y)update(rc(d),mid+1,r,x,y,k);
    tr[d]=tr[lc(d)]+tr[rc(d)];
}
void build(ll d,ll l,ll r)
{
    if(l==r){tr[d]=1;return;}
    ll mid=md(l,r);
    build(lc(d),l,mid);build(rc(d),mid+1,r);
    tr[d]=tr[lc(d)]+tr[rc(d)];
}
ll query(ll d,ll l,ll r,ll x,ll y)
{
    if(l>y||x>r)return 0;if(x<=l&&r<=y)return tr[d];
    ll mid=md(l,r),ret=0;
    pushdown(d,l,r,mid);
    if(mid>=x)ret+=query(lc(d),l,mid,x,y); if(mid<y)ret+=query(rc(d),mid+1,r,x,y);
    return ret;
}
inline ll efcz(ll x,ll y,ll k)
{
    if (x>y) return -1;
    ll l=0,r=y-x,mid;
    while (l<=r)
    {
        mid=md(l,r);
        if(query(1,1,n,x,x+mid)==k*(mid+1))l=mid+1;
        else r=mid-1;
    }
    return l;
}
inline void work1(){ll t1=read(),t2=read();update(1,1,n,t1,t2,0);return;}
inline void work2()
{
    ll t1=read(),t2=read(),t3=read(),t4=read();
    int num=query(1,1,n,t1,t2),p=0;
    update(1,1,n,t1,t2,0);
    if (num>=t4-t3+1-query(1,1,n,t3,t4))update(1,1,n,t3,t4,1);
    else
    {
        if(num!=0)
        {
            while(1)
            {
                if (query(1,1,n,t3,t3)==1){p=efcz(t3,t4,1);t3+=p;}
                p=efcz(t3,t4,0);
                if(p==-1)break;
                if (p>num){update(1,1,n,t3,t3+num-1,1);break;}
                update(1,1,n,t3,t3+p-1,1);num-=p;t3+=p;
            }
        }
    }
}
inline void work3()
{
    ll t1=read(),t2=read();
    ll ans=0,p=0;
    while(1)
    {
        if (query(1,1,n,t1,t1)==1){p=efcz(t1,t2,1);t1+=p;}
        p=efcz(t1,t2,0);
        if(p==-1)break;
        ans=max(ans,p);t1+=p;
        if(t1>t2)break;
    }
    printf("%lld\n",ans);
}

int main()
{
    n=read();m=read();build(1,1,n);
    while(m--){ll t=read();if(t==0)work1();if(t==1)work2();if(t==2)work3();}
    return 0;
}
想起来还要放个代码呢QAQ

 

猜你喜欢

转载自www.cnblogs.com/lqsukida/p/10048113.html