先%一下图灵, 肽巨(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;
}