题目链接:https://codeforces.com/problemset/problem/1270/H
给一个长度为 n 的数组 a,a 中的元素两两不同。
对于每个数对 (i,j)(i<j),若 ai<aj,则让 i 向 j 连一条边。求图中连通块个数。
支持 q 次修改数组某个位置的值,每次修改后输出图中连通块个数。
n,q≤5×105,1≤ai≤106,保证任意时刻数组中元素两两不同。
有一个性质:一个连通块一定是序列里的一个区间。
设 i<j,k∈(i,j),且 i,j 在一个连通块内:
- 如果 ai<aj,那么显然至少满足 ai<ak 或 aj>ak 中的一个。
- 如果 ai>aj,必然存在 x<i 且 ax<aj<ai 或者 y>j 且 ay>ai>aj,拿前者举例,[x,i] 与 [x,j] 都必然在同一个连通块内,[i,j] 也就在同一个连通块内。
那么问题就转化为有多少个位置 p 满足 min(a1⋯ap−1)>max(ap⋯an)。
考虑枚举 v=max(ap⋯an),可以把 >v 的设为 1,≤v 的设为 0,那么这个 v 能分割两个连通块当且仅当这个 01 序列单调不增。
设 a0=+∞,an+1=0,那么对于任意的 v,其 01 序列至少包含一个 0 和一个 1,那么问题又可以转化为求有多少个 v 满足其对应的 01 序列只存在一组 0,1 相邻。
对值域建立一棵线段树,对于相邻的元素 ai,ai+1,他们能对 [min(ai,ai+1),max(ai,ai+1)) 里面的 v 贡献一组相邻的 0,1。那么就将这个区间加 1。每次询问只需要查询有多少个在序列里的数字线段树上只有 1 点贡献即可。
时间复杂度 O((Q+n)logV)。
#include <bits/stdc++.h>
using namespace std;
const int N=1000010,lim=1e6+1;
int n,Q,a[N];
struct SegTree
{
int cnt[N*4],minv[N*4],lazy[N*4];
void pushdown(int x)
{
if (!lazy[x]) return;
minv[x*2]+=lazy[x]; minv[x*2+1]+=lazy[x];
lazy[x*2]+=lazy[x]; lazy[x*2+1]+=lazy[x];
lazy[x]=0;
}
void pushup(int x)
{
minv[x]=min(minv[x*2],minv[x*2+1]); cnt[x]=0;
if (minv[x]==minv[x*2]) cnt[x]+=cnt[x*2];
if (minv[x]==minv[x*2+1]) cnt[x]+=cnt[x*2+1];
}
void update1(int x,int l,int r,int ql,int qr,int v)
{
if (ql<=l && qr>=r) { minv[x]+=v; lazy[x]+=v; return; }
pushdown(x);
int mid=(l+r)>>1;
if (ql<=mid) update1(x*2,l,mid,ql,qr,v);
if (qr>mid) update1(x*2+1,mid+1,r,ql,qr,v);
pushup(x);
}
void update2(int x,int l,int r,int k,int v)
{
if (l==r) { cnt[x]+=v; return; }
pushdown(x);
int mid=(l+r)>>1;
if (k<=mid) update2(x*2,l,mid,k,v);
if (k>mid) update2(x*2+1,mid+1,r,k,v);
pushup(x);
}
int query(int x,int l,int r,int ql,int qr)
{
if (ql<=l && qr>=r) return (minv[x]==1)?cnt[x]:0;
pushdown(x);
int mid=(l+r)>>1,res=0;
if (ql<=mid) res+=query(x*2,l,mid,ql,qr);
if (qr>mid) res+=query(x*2+1,mid+1,r,ql,qr);
return res;
}
}seg;
int main()
{
scanf("%d%d",&n,&Q);
a[0]=lim;
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
seg.update1(1,0,lim,min(a[i],a[i-1]),max(a[i],a[i-1])-1,1);
seg.update2(1,0,lim,a[i],1);
}
seg.update1(1,0,lim,0,a[n]-1,1);
while (Q--)
{
int x,y;
scanf("%d%d",&x,&y);
seg.update1(1,0,lim,min(a[x],a[x-1]),max(a[x],a[x-1])-1,-1);
seg.update1(1,0,lim,min(a[x],a[x+1]),max(a[x],a[x+1])-1,-1);
seg.update2(1,0,lim,a[x],-1);
a[x]=y;
seg.update1(1,0,lim,min(a[x],a[x-1]),max(a[x],a[x-1])-1,1);
seg.update1(1,0,lim,min(a[x],a[x+1]),max(a[x],a[x+1])-1,1);
seg.update2(1,0,lim,a[x],1);
printf("%d\n",seg.query(1,0,lim,1,lim-1));
}
return 0;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
2021-06-15 【洛谷P6378】Riddle
2021-06-15 【CF375D】Tree and Queries
2021-06-15 【CF1063F】String Journey
2020-06-15 【LOJ#2255】炸弹