【CF1270H】Number of Components

1|0题目


题目链接:https://codeforces.com/problemset/problem/1270/H
给一个长度为 n 的数组 aa 中的元素两两不同。
对于每个数对 (i,j)(i<j),若 ai<aj,则让 ij 连一条边。求图中连通块个数。
支持 q 次修改数组某个位置的值,每次修改后输出图中连通块个数。
n,q5×105,1ai106,保证任意时刻数组中元素两两不同。

2|0思路


有一个性质:一个连通块一定是序列里的一个区间。
i<jk(i,j),且 i,j 在一个连通块内:

  • 如果 ai<aj,那么显然至少满足 ai<akaj>ak 中的一个。
  • 如果 ai>aj,必然存在 x<iax<aj<ai 或者 y>jay>ai>aj,拿前者举例,[x,i][x,j] 都必然在同一个连通块内,[i,j] 也就在同一个连通块内。

那么问题就转化为有多少个位置 p 满足 min(a1ap1)>max(apan)
考虑枚举 v=max(apan),可以把 >v 的设为 1v 的设为 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)

3|0代码


#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__

本文作者stoorz
本文链接https://www.cnblogs.com/stoorz/p/16378647.html
关于博主:菜死了 /fad
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   stoorz  阅读(74)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 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】炸弹
点击右上角即可分享
微信分享提示