华东交通大学2021年ACM“双基”程序设计竞赛 F 线段树 二分出合法区间

题目

在这里插入图片描述

题解思路

看的出是线段树,一开始想用线段树的操作来模拟操作一,但是好像弄不出。
这里就有个性质了,当你要修改某段为1的时候你查询它的总和不为它的长度时,只需将总和改成长度然后pd一下就行了。
但是这个从哪里开始哪里结束要怎么返回呢?
利用前缀和的单调性就能得到这段有几个被填上了的。
所以我们可以进行二分,二分出那个没被填上的第一个点,以及二分出填的时候结束的位置(这里利用上面的性质,我们只需知道两个端点将他们的sum改成长度然后pd即可)
这里的二分属实巧妙啊

            int ans1 = n + 1  , ans2 = n+1 ; 
            int l = t2 , r = n ;
            while ( l <= r )
            {
                int mid = l+r >> 1 ;
                int sum = sea(1,l,mid) ;
                if ( sum == mid - l + 1 )
                    l = mid + 1 ;
                else
                    r = mid - 1 , ans1 = mid ; 
            }
            l = ans1 , r = n ; 
            while ( l <= r )
            {
                int mid = l + r >> 1 ;
                int sum =  mid - ans1 + 1 - sea(1,ans1,mid);
                if (sum >= t3 )
                    r = mid - 1 , ans2 = mid ;
                else
                    l = mid + 1 ;
            }
            if (ans1 > n || ans2 > n )
                cout << "0\n";
            else
                cout << ans1 << " " << ans2 << "\n" ;
            _add(1,min(n,ans1),min(n,ans2),1) ; 

这里对线段树的操作倒是没有多少,pd为2sum改成0,pd为1改成长度。
线段树的题写太少了。

AC代码
#include <bits/stdc++.h>
//#include <unordered_map>
//priority_queue
#define PII pair<int,int>
#define ll long long

using namespace std;

const  int  INF =  0x3f3f3f3f;

struct node
{
    int  sum;
    int l,r,lz;
};
const int N = 1001000 ;
node tee[4*N];

int n , cnt = 0 , m ;

void  in(int i , int t1 , int t2)
{
    tee[i].l = t1 , tee[i].r = t2;
    if ( t1 == t2 )
    {
        tee[i].sum = 0;
        tee[i].lz = 0 ;
        return ;
    }
    int j = (t1+t2)/2;
    in(i*2,t1,j);
    in(i*2+1,j+1,t2);
    tee[i].sum = tee[i*2].sum+tee[i*2+1].sum;
    tee[i].lz = 0 ;
}
void pd(int i )
{
    if ( tee[i].lz != 0 )
    {
        tee[i*2].lz = tee[i].lz;
        tee[i*2+1].lz = tee[i].lz;
        tee[i*2].sum = tee[i*2].lz == 1 ? tee[i*2].r - tee[i*2].l + 1 : 0 ;
        tee[i*2+1].sum = tee[i*2+1].lz == 1 ? tee[i*2+1].r - tee[i*2+1].l + 1 : 0 ;
        tee[i].lz = 0 ;
    }
}
long long sea( int i ,int t1 , int t2 )
{
    if ( tee[i].l >= t1 && tee[i].r <= t2 )
        return tee[i].sum;
    pd(i);
    long long sum = 0 ;
    if ( tee[i*2].r >= t1 )
        sum += sea(i*2,t1,t2);
    if ( tee[i*2+1].l <= t2 )
        sum += sea(i*2+1,t1,t2);
    return sum;
}
void _add( int i , int t1 , int t2 ,int vv )
{
    int l = tee[i].l , r = tee[i].r ; 
    if  ( l >= t1  && r <= t2 )
    {
        tee[i].lz = vv ; 
        tee[i].sum = tee[i].lz == 1 ? r - l + 1 : 0 ;
        return  ; 
    }
    pd(i);
    if ( tee[i*2].r >= t1)
        _add(i*2,t1,t2,vv);
    if (tee[i*2+1].l <= t2)
        _add(i*2+1,t1,t2,vv);
    tee[i].sum = tee[i*2].sum + tee[i*2+1].sum;
}

int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin >> n >> m ;
    in(1,1,n) ; 
    for (int i = 1 ; i <= m ; i++ )
    {
        int t1 ,t2 , t3 ;
        cin >> t1 >> t2 >> t3 ; 
        if ( t1 == 1 )
        {
            int ans1 = n + 1  , ans2 = n+1 ; 
            int l = t2 , r = n ;
            while ( l <= r )
            {
                int mid = l+r >> 1 ;
                int sum = sea(1,l,mid) ;
                if ( sum == mid - l + 1 )
                    l = mid + 1 ;
                else
                    r = mid - 1 , ans1 = mid ; 
            }
            l = ans1 , r = n ; 
            while ( l <= r )
            {
                int mid = l + r >> 1 ;
                int sum =  mid - ans1 + 1 - sea(1,ans1,mid);
                if (sum >= t3 )
                    r = mid - 1 , ans2 = mid ;
                else
                    l = mid + 1 ;
            }
            if (ans1 > n || ans2 > n )
                cout << "0\n";
            else
                cout << ans1 << " " << ans2 << "\n" ;
            _add(1,min(n,ans1),min(n,ans2),1) ; 
        }else
        {
            cout << sea(1,t2,t3) << "\n" ;
            _add(1,t2,t3,2) ; 
        }
    }
    return 0 ;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值