luogu 5251 图灵机二代 珂朵莉树+树状数组+线段树+ 双指针

先%一下图灵, 肽巨(exin)了

(然而我并不想像wlj一样, 把放空间里供着。。。)

借鉴了一下前几位巨佬的思路。

思想:(我感觉解释的比较详细)

第一种操作 :单点修改数字 /应该不用多说

第二种操作: 区间颜色推平。。。显然要请出我们可耐的珂朵莉(old driver)啊!!!

第三种操作 : 包含所有颜色 数字和最小的子区间 de数字和

第四种操作: 不含重复颜色,数字和最大的子区间的数字和

3, 4 貌似不能维护 所以要单拿出来搞

考虑双指针法(其实很难考虑到这种方法)
现在我们把所有颜色相同的一段连续区间放在了一个块里(ODT)
现在对这些块用双指针

3、

一开始左右指针都指向左端点, 现在要找包含所有颜色 数字和最小的子区间 de数字和
显然我们要用双指针维护|包含所有的颜色|;

​ 对于每一次统计答案的过程:

​ 每次让右指针向右移直到出现了所有的颜色, 此时再考虑左指针是不是也能向右移一点,直到左指针不能向右移

4、

一开始左右指针都指向左端点, 现在要找的是不含重复颜色,|数字和最大的子区间 de数字和| 显然我们要用双指针维护|不含重复颜色|;
显然一个颜色是无论如何也合法的 所以答案初值赋成区间最大值(用数据结构维护)

​ 对于每一次统计答案的过程:

​ 此时枚举右指针的位置, 看左指针最小会到哪, 找到第一个合法的位置, 统计答案;

大概思路就是这样,还有一些珂朵莉的细节要注意一下(比如它返回的哪一块的迭代器),而且很繁琐。。。(表示看到差点吐血)

具体可以看代码以及注释

(一个下午+ 一个晚上。。保证自己一个一个打的)

丑陋的代码 大括号换行之类的就别在意了

#include 
#include 
#include  
#define IT set::iterator
#define RR register
#define N 100001
using namespace std;
int n, m ,c, num[N];
inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0'||ch > '9')
        { if(ch == '-') f = -f; ch = getchar(); }
    while(ch >= '0'&&ch <= '9')
        {x = (x<<1) + (x<<3) + ch-'0'; ch = getchar(); }
    return f * x;
}
/******************************************************************/
//树状数组  维护区间和 不解释 
struct Bittree
{
    int val[N];
    inline int lowbit(int x){return x&(-x);}
    inline int ask(RR int pos)
    {
        int res = 0;
        for(RR int i = pos; i; i -= lowbit(i))
             res += val[i];
        return res;
    }
    inline void chenge(RR int pos, RR int v)
    {
        for(RR int i = pos; i <= n; i += lowbit(i))
           val[i] += v;
    }
    inline int ask_(RR int l, RR int r)
    {
        return ask(r) - ask(l - 1);
    }
}tr1;
//线段树 维护区间最大值 不解释 
struct Segment
{
    struct node
    {
        int l, r, maxx;
        node *L, *R;
        node(int l, int r) : l(l), r(r), maxx(0), L(NULL), R(NULL) {}
        inline int mid(){return (l + r)>>1; }
        inline int len(){return (r - l + 1); }
        inline void up() {
            maxx = max(L->maxx , R->maxx);
        }
    }*root;
    
    inline void build(RR node *&k, RR int l, RR int r)
    {
        k =new node(l, r);
        if(l == r)  {
            k->maxx = num[l];
            return ;
        }
        int mid = k->mid();
        build(k->L, k->l, mid);
        build(k->R, mid + 1, r);
        k->up();
    }
    inline void chenge(RR node *k, RR int pos, RR int val)
    {
        if(k->l == k->r)
        {
            k->maxx = val;
            return;
        }
        int mid = k->mid();
        if(pos <= mid)      chenge(k->L, pos, val);
        if(pos >= mid +1 )  chenge(k->R, pos, val);
        k->up();
    }
    inline int ask(RR node *k, RR int l, RR int r)
    {
        if(l<= k->l && r >= k->r)
            return k->maxx;
        int res = 0;
        int mid = k->mid();
        if(l <= mid)     res = max(res, ask(k->L, l, r));
        if(r >= mid + 1) res = max(res, ask(k->R, l, r));
        return res;
    }
}tr2;
// ODT  emmm 
struct block
{
    int l, r;
    mutable int col;
    block(int l , int r = -1, int col =0) : l(l), r(r), col(col) {}
    friend bool operator < (const block &a, const block &b)
    {
        return a.l < b.l;
    }
};
set< block >s;
IT split(int pos)//基本操作 
{
    IT it = s.lower_bound(block(pos));
    if(it != s.end() && it->l == pos)
      return it;
    it--;
    int ll = it->l, rr = it->r;
    int col = it->col;
    s.erase( it );
    s.insert( block(ll, pos - 1, col) );
    return s.insert( block(pos, rr, col) ).first;
}
bool check(IT l, IT r) 
//   不重复的要求 :要么在一个块  要么邻块  要么之间的所有块都只有一个元素  
{
    if(l == r || (++l)-- ==r) return 1;
    ++ l;
    for(IT i = l; i != r; ++ i)
    {
        if(i->r != i->l)return 0;
    }
    return 1;
}
inline void assign(RR int l, RR int r, RR int col)//基本操作 
{
    IT itr = split(r+1), itl = split(l);
    s.erase(itl, itr);
    s.insert(block(l, r, col)); 
}
/**************************************************************/
int tmp[N];//颜色桶 
inline int zh_dou(RR int l, RR int r)// 包含所有颜色  数字和最小的子区间 
{
    int ans = 2e9, rest = c;//没统计上的颜色数 
    for(int i = 1; i <= c; i++) tmp[i] = 0;
    IT itr = split(r+1), itl = split(l), ll, rr;//不透彻的自己学ODT去 
    --itl;  ll = rr = itl;
    while(rr != itr)//双指针  若每次操作后右指针未到区间右端 
    {
        if(ll != itl)// 这个地方的确难理解...e xin了我好久
        //考虑上一轮 左指针向左移 直到少一种颜色, 但是你后来又加上了, 
        //现在要再悔回去  因为本轮右指针右移的前提是还有未统计的颜色 想想看  
        {
            --tmp[ll->col];
            if(!tmp[ll->col]) ++rest;
        }ll++;
        while(rest && rr != itr)//还有未统计的颜色 且 右指针能往右移 
        {
            ++rr; 
            ++tmp[rr->col];
            if(tmp[rr->col] == 1)--rest; //当这个颜色之前未统计过才能算贡献 
        }
        if(rr==itr) break;//到右端点就break掉  此时不能统计答案 
        while(!rest && ll!=rr)//颜色统计全了后考虑将左指针向左移 直到少一种颜色 
        {
            --tmp[ll->col];
            if(!tmp[ll->col]) ++rest; //此时少了一种颜色 
            ++ll;
        }
        if(rest)//将刚刚少的那个颜色返回去 
        {
            --ll;  ++ tmp[ll->col]; --rest;
        }
        ans = min(ans, tr1.ask_(ll->r, rr->l));
        //此时就找到一种合法情况,可以统计一次答案,数字和最小,所以所以是左端块的右端点 到 右端块的左端点 
    } 
    return  ans == 2e9 ? -1 : ans; 
}
inline int w_lj( RR int l, RR int r)// 不含重复颜色,数字和最大的子区间 
{
    for(int i = 1; i <= c; i++) tmp[i] = 0;
    int ans = tr2.ask(tr2.root, l, r); //赋为区间最大值 
    IT itr = split(r + 1), itl = split(l), ll, rr;//不用解释 
    rr = itl; ll = itl;
    while(rr != itr)//枚举右指针的位置 
    {
        ++tmp[rr->col];//对应上一轮的最后rr++ 
        while(!check(ll, rr)) //不符合不重复的要求   就把左指针往右移 
        --tmp[ll->col], ++ll; 
        while(ll!=rr && tmp[rr->col] > 1) --tmp[ll->col], ++ll;//不光不能有长度大于1 的块 还不能有颜色相同的块 
        if(ll!=rr) ans = max(ans, tr1.ask_(ll->r,  rr->l)); //此时就可以统计答案了 (合法了) 
        ++rr;// 到下一轮 
    }
    return ans;

}
signed main()//下面应该不用解释 
{
    int  l, r, x, y, opt;
    n = read(); m = read(); c = read();
    s.insert(block(0, 0, -1)); s.insert(block(n + 1, n + 1, -1));
    for(RR int i = 1; i <= n; i ++)
    {
        num[i] = read();
        tr1.chenge(i, num[i]);
    }
    tr2.build(tr2.root, 1, n);
    for(RR int i = 1; i <=n; i ++)
    {
        int color = read();
        s.insert(block(i, i, color));
    }
    while(m --> 0)
    {
        opt = read();
        if(opt == 1)
        {
            x = read(); y = read();
            tr1.chenge(x, y - tr1.ask_(x, x));
            tr2.chenge(tr2.root, x, y);
        }
        else if(opt == 2)
        {
            l = read(); r = read(); x = read();
            assign(l, r, x);
        }
        else if(opt == 3)
        {
            l = read(); r = read();
            int ans = zh_dou(l, r);
            printf("%d\n", ans);
        }
        else 
        {
            l = read(); r = read();
            int ans = w_lj(l, r);
            printf("%d\n",ans);
        }   
    }
    return 0;
}
// 本题的卡常 
/*
1 inline rejister
2 数组开小点
3 赋初值尽量改for, 不用memset 
4 换快读, 或fread 
5 可以尝试用指针
*/
 

原代码

#include 
#include 
#include  
#define IT set::iterator
#define RR register
#define N 100001
using namespace std;
int n, m ,c, num[N];
inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0'||ch > '9')
        { if(ch == '-') f = -f; ch = getchar(); }
    while(ch >= '0'&&ch <= '9')
        {x = (x<<1) + (x<<3) + ch-'0'; ch = getchar(); }
    return f * x;
}
/******************************************************************/
//树状数组
struct Bittree
{
    int val[N];
    inline int lowbit(int x){return x&(-x);}
    inline int ask(RR int pos)
    {
        int res = 0;
        for(RR int i = pos; i; i -= lowbit(i))
             res += val[i];
        return res;
    }
    inline void chenge(RR int pos, RR int v)
    {
        for(RR int i = pos; i <= n; i += lowbit(i))
           val[i] += v;
    }
    inline int ask_(RR int l, RR int r)
    {
        return ask(r) - ask(l - 1);
    }
}tr1;
//线段树 
struct Segment
{
    struct node
    {
        int l, r, maxx;
        node *L, *R;
        node(int l, int r) : l(l), r(r), maxx(0), L(NULL), R(NULL) {}
        inline int mid(){return (l + r)>>1; }
        inline int len(){return (r - l + 1); }
        inline void up() {
            maxx = max(L->maxx , R->maxx);
        }
    }*root;
    
    inline void build(RR node *&k, RR int l, RR int r)
    {
        k =new node(l, r);
        if(l == r)  {
            k->maxx = num[l];
            return ;
        }
        int mid = k->mid();
        build(k->L, k->l, mid);
        build(k->R, mid + 1, r);
        k->up();
    }
    inline void chenge(RR node *k, RR int pos, RR int val)
    {
        if(k->l == k->r)
        {
            k->maxx = val;
            return;
        }
        int mid = k->mid();
        if(pos <= mid)chenge(k->L, pos, val);
        else if(pos >= mid +1 )chenge(k->R, pos, val);
        k->up();
    }
    inline int ask(RR node *k, RR int l, RR int r)
    {
        if(l<= k->l && r >= k->r)
            return k->maxx;
        int res = 0;
        int mid = k->mid();
        if(l <= mid) res = max(res, ask(k->L, l, r));
        if(r >= mid + 1) res = max(res, ask(k->R, l, r));
        return res;
    }
}tr2;
// ODT
struct block
{
    int l, r;
    mutable int col;
    block(int l , int r = -1, int col =0) : l(l), r(r), col(col) {}
    friend bool operator < (const block &a, const block &b)
    {
        return a.l < b.l;
    }
};
set< block >s;
IT split(int pos)
{
    IT it = s.lower_bound(block(pos));
    if(it != s.end() && it->l == pos)
      return it;
    it--;
    int ll = it->l, rr = it->r;
    int col = it->col;
    s.erase( it );
    s.insert( block(ll, pos - 1, col) );
    return s.insert( block(pos, rr, col) ).first;
}
bool only (IT l, IT r)
{
    if(l == r||(++l)--==r)return 1;
    ++l;
    for(IT i = l; i != r; ++ i)
    {
        if(i->r != i->l)return 0;
    }
    return 1;
}
inline void assign(RR int l, RR int r, RR int col)
{
    IT itr = split(r+1), itl = split(l);
    s.erase(itl, itr);
    s.insert(block(l, r, col)); 
}
/**************************************************************/
int tmp[N];
inline int zh_dou(RR int l, RR int r) 
{
    int ans = 2e9, rest = c;
//  memset(tmp, 0, sizeof tmp);
    for(int i = 1; i <= c; i++) tmp[i] = 0;
    IT itr = split(r+1), itl = split(l), ll, rr;
    --itl;  ll = rr = itl;
    while(rr != itr)
    {
        if(ll != itl)
        {
            --tmp[ll->col];
            if(!tmp[ll->col]) ++rest;
        }ll++;
        while(rest && rr != itr)
        {
            ++rr; ++tmp[rr->col];
            if(tmp[rr->col] == 1)  --rest;
        }
        if(rr==itr) break;
        while(!rest && ll!=rr)
        {
            --tmp[ll->col];
            if(!tmp[ll->col])
               ++rest;
            ++ll;
        }
        if(rest)
        {
            --ll;  ++ tmp[ll->col]; --rest;
        }
        ans = min(ans, tr1.ask_(ll->r, rr->l)); 
    } 
    return  ans == 2e9 ? -1 : ans; 
}
inline int w_lj( RR int l, RR int r) 
{
//  memset(tmp, 0, sizeof tmp);
    for(int i = 1; i <= c; i++) tmp[i] = 0;
    int ans = tr2.ask(tr2.root, l, r);
    IT itr = split(r + 1), itl = split(l), ll, rr;
    rr = itl; ll = itl;
    while(rr != itr)
    {
        ++tmp[rr->col];
        while(!only(ll, rr))  --tmp[ll->col], ++ll;
        while(ll!=rr && tmp[rr->col] > 1) --tmp[ll->col], ++ll;
        if(ll!=rr) ans = max(ans, tr1.ask_(ll->r,  rr->l)); 
        ++rr;
    }
    return ans;

}
signed main()
{
    int  l, r, x, y, opt;
    n = read(); m = read(); c = read();
    s.insert(block(0, 0, -1)); s.insert(block(n + 1, n + 1, -1));
    for(RR int i = 1; i <= n; i ++)
    {
        num[i] = read();
        tr1.chenge(i, num[i]);
    }
    tr2.build(tr2.root, 1, n);
    for(RR int i = 1; i <=n; i ++)
    {
        int color = read();
        s.insert(block(i, i, color));
    }
    while(m --> 0)
    {
        opt = read();
        if(opt == 1)
        {
            x = read(); y = read();
            tr1.chenge(x, y - tr1.ask_(x, x));
            tr2.chenge(tr2.root, x, y);
        }
        else if(opt == 2)
        {
            l = read(); r = read(); x = read();
            assign(l, r, x);
        }
        else if(opt == 3)
        {
            l = read(); r = read();
            int ans = zh_dou(l, r);
            printf("%d\n", ans);
        }
        else 
        {
            l = read(); r = read();
            int ans = w_lj(l, r);
            printf("%d\n",ans);
        }   
    }
    return 0;
}

你可能感兴趣的:(luogu 5251 图灵机二代 珂朵莉树+树状数组+线段树+ 双指针)