CF1209G2 Into Blocks (hard version) 题解
思路比较困难的一道线段树题目。
考虑对于一段一段的混合颜色区间,我们的最优策略显然是留下其中元素数量最多的颜色。
比如:
12221123343
这就是两个颜色区间,一个是
其中第一个留下
考虑利用线段树来维护这个东西。
我们考虑将每个元素维护
可以利用
维护一个
对于每个颜色,就在
可以发现,
那么我们同时也可以在每一个颜色的起始位置上存放这个颜色的元素个数。
我们就可以维护这里面每一个区间的最大值。
使用线段树来维护。
讲一下实现细节。
维护一个
可以很简单的发现,如果一个区间里面
所以我们可以根据有零的区间来讨论。
-
如果
也就是零在左边。那么,这个区间靠右的最大值是右区间的最大值和左区间靠右的最大值的最大值,最大值之和就是左区间的最大值之和。
-
如果
也就是零在右边。那么,这个区间靠左的最大值是左区间的最大值和右区间靠左的最大值的最大值,最大值之和就是右区间的最大值之和。
-
如果
也就是两边都有零。那么,最大值之和就是右区间的最大值之和和左区间的最大值之和和左区间靠右的最大值与右区间靠左的最大值中的最大值之和。
其他的就可以直接看代码了。
#include <bits/stdc++.h>
using namespace std;
const int N = 200010;
int n , m , a[N];
set<int> q[N];
struct Tree
{
int l , r;
int ls , rs , sum;
int minn , maxx , tag;
}t[N * 4];
inline int read()
{
int asd = 0 , qwe = 1; char zxc;
while(!isdigit(zxc = getchar())) if(zxc == '-') qwe = -1;
while(isdigit(zxc)) asd = asd * 10 + zxc - '0' , zxc = getchar();
return asd * qwe;
}
inline void build(int p , int l , int r)
{
t[p].l = l , t[p].r = r;
if(l == r) return;
build(p * 2 , l , (l + r) / 2);
build(p * 2 + 1 , (l + r) / 2 + 1 , r);
}
inline void pushDown(int p)
{
t[p * 2].minn += t[p].tag;
t[p * 2 + 1].minn += t[p].tag;
t[p * 2].tag += t[p].tag;
t[p * 2 + 1].tag += t[p].tag;
t[p].tag = 0;
}
inline void pushUp(int p)
{
int l = p * 2 , r = p * 2 + 1;
t[p].minn = min(t[l].minn , t[r].minn);
t[p].maxx = max(t[l].maxx , t[r].maxx);
if(t[l].minn < t[r].minn)
{
t[p].ls = t[l].ls;
t[p].sum = t[l].sum;
t[p].rs = max(t[l].rs , t[r].maxx);
}
if(t[l].minn > t[r].minn)
{
t[p].rs = t[r].rs;
t[p].sum = t[r].sum;
t[p].ls = max(t[r].ls , t[l].maxx);
}
if(t[l].minn == t[r].minn)
{
t[p].rs = t[r].rs;
t[p].ls = t[l].ls;
t[p].sum = t[l].sum + t[r].sum + max(t[l].rs , t[r].ls);
}
}
inline void update1(int p , int l , int r , int k)
{
if(l > r) return;
if(l <= t[p].l && t[p].r <= r)
{
t[p].minn += k;
t[p].tag += k;
return;
}
pushDown(p);
int mid = (t[p].l + t[p].r) / 2;
if(l <= mid) update1(p * 2 , l , r , k);
if(r > mid) update1(p * 2 + 1 , l , r , k);
pushUp(p);
}
inline void update2(int p , int x , int k)
{
if(t[p].l == t[p].r)
{
t[p].maxx += k;
t[p].ls += k;
return;
}
pushDown(p);
int mid = (t[p].l + t[p].r) / 2;
if(x <= mid) update2(p * 2 , x , k);
if(x > mid) update2(p * 2 + 1 , x , k);
pushUp(p);
}
inline void updateUsed(int col , int k)
{
int siz = q[col].size();
if(!siz) return;
update1(1 , *q[col].begin() , *(--q[col].end()) - 1 , k);
update2(1 , *q[col].begin() , k * siz);
}
inline int ask()
{
return n - t[1].sum - t[1].ls - t[1].rs;
}
int main()
{
int maxA = 0;
n = read() , m = read();
for(int i = 1;i <= n;i++)
a[i] = read() , q[a[i]].insert(i),
maxA = max(maxA , a[i]);
build(1 , 1 , n);
for(int i = 1;i <= maxA;i++)
if(!q[i].empty())
updateUsed(i , 1);
printf("%d\n" , ask());
for(int i = 1;i <= m;i++)
{
int x = read() , y = read();
int col = a[x];
updateUsed(col , -1) , q[col].erase(x);
updateUsed(col , 1) , updateUsed(y , -1);
q[y].insert(x) , updateUsed(y , 1) , a[x] = y;
printf("%d\n" , ask());
}
return 0;
}
作者:JiaY19
出处:https://www.cnblogs.com/JiaY19/p/16755486.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)