BZOJ5312 冒险 势能分析、线段树

传送门


区间位赋值、区间求最大值似乎是不能够像一般的线段树一样直接打标记的,但是直接暴力也太没有面子了

我们考虑优化一下暴力:如果说线段树的一段区间内在当前修改的所有位置上所有数都是相同的,那么这个最大值就是可以直接维护的,在上面打上标记;如果这个条件不满足就暴力向下递归。

然后交一发发现过了!然而这并不是数据水。

考虑势能分析计算复杂度。设每一个节点的势能函数为当前区间的所有数在位置上不全相同的位置个数。每一次在找到覆盖区间之后暴力向下递归,则势能函数必定减小至少\(1\),而势能的增加量最多为\(log\ nlog\ w\),所以总的复杂度是\(O(nlog\ nlog\ w)\)

#include<bits/stdc++.h>
using namespace std;

int read(){
    int a = 0; char c = getchar(); bool f = 0;
    while(!isdigit(c)){f = c == '-'; c = getchar();}
    while(isdigit(c)){
        a = a * 10 + c - 48; c = getchar();
    }
    return f ? -a : a;
}

const int _ = 2e5 + 3 , MX = (1 << 20) - 1;
namespace segt{
    int mx[_ << 2] , mrkand[_ << 2] , mrkor[_ << 2] , vis[_ << 2];
    
#define mid ((l + r) >> 1)
#define lch (x << 1)
#define rch (x << 1 | 1)

    void mark(int x , int And , int Or){
        mrkor[x] ^= mrkor[x] & And; mrkand[x] ^= mrkand[x] & Or;
        mrkor[x] |= Or; mrkand[x] |= And; mx[x] = mx[x] & ~And | Or;
    }
    void down(int x){mark(lch , mrkand[x] , mrkor[x]); mark(rch , mrkand[x] , mrkor[x]); mrkand[x] = mrkor[x] = 0;}
    
    void init(int x , int l , int r){
        if(l == r){mx[x] = read(); vis[x] = MX;}
        else{
            init(lch , l , mid); init(rch , mid + 1 , r);
            mx[x] = max(mx[lch] , mx[rch]); vis[x] = vis[lch] & vis[rch] & ~(mx[lch] ^ mx[rch]);
        }
    }
    
    void modify(int x , int l , int r , int L , int R , int And , int Or){
        if(l >= L && r <= R && ((vis[x] & And) == And) && ((vis[x] & Or) == Or)){mark(x , And , Or); return;}
        down(x); if(mid >= L) modify(lch , l , mid , L , R , And , Or);
        if(mid < R) modify(rch , mid + 1 , r , L , R , And , Or);
        mx[x] = max(mx[lch] , mx[rch]); vis[x] = vis[lch] & vis[rch] & ~(mx[lch] ^ mx[rch]);
    }

    int qry(int x , int l , int r , int L , int R){
        if(l >= L && r <= R) return mx[x];
        int mx = 0; down(x);
        if(mid >= L) mx = qry(lch , l , mid , L , R);
        if(mid < R) mx = max(mx , qry(rch , mid + 1 , r , L , R));
        return mx;
    }
}using namespace segt;

int main(){
#ifndef ONLINE_JUDGE
    freopen("in","r",stdin);
    freopen("out","w",stdout);
#endif
    int N = read() , M = read() , l , r , x; init(1 , 1 , N);
    while(M--)
        switch(read()){
        case 1:
            l = read(); r = read(); x = read(); modify(1 , 1 , N , l , r , MX ^ x , 0);
            break;
        case 2:
            l = read(); r = read(); x = read(); modify(1 , 1 , N , l , r , 0 , x);
            break;
        case 3:
            l = read(); r = read(); printf("%d\n" , qry(1 , 1 , N , l , r));
        }
    return 0;
}

转载于:https://www.cnblogs.com/Itst/p/11508387.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值