Soratosorato

ABC356F Distance Component Size Query 题解

Sorato·2024-06-25 14:32·19 次阅读

ABC356F Distance Component Size Query 题解

ABC356F Distance Component Size Query

题目大意#

q 次操作和一个无向图 G,以及一个常数 k。操作有两种:

  • 给定 x,若 xG 则从 G 中删去 x,否则将 x 插入 G,并将 x 与所有 y[xk,x+k]G 连边。
  • 给定 x,求 Gx 所在连通块的大小。

Solve#

若用一个有序集合 S 维护 G 中的点,并对其作差分,令差分后的集合为 C,每次操作 2 相当于查询 Cx 左侧第一个 >k 的数的位置 L,以及 x 右侧第一个 >k 的数的位置 R,答案即为 RLR 不计入区间,L 计入)。

如何维护 S

考虑把操作离线,离散化所有 x。然后以离散化后的 x 为下标建立线段树,值为 Cx 对应的差值。即:若 Sx 位于 p,则线段树上以 x 为下标的值为 Cp

对于线段树上的每个节点 [l,r],我们再记录 mx[l,r] 上的最大值,sum[l,r] 上有多少下标已被插入 S

修改:

  • 对于插入操作:先将 x 插入 S,再查询 Sx 的位置 p,单点修改 xSpSp1Sp+1Sp+1x
  • 对于删除操作:查询 Sx 的位置 p,单点修改 Sp+1Sp+1Sp1

查询:

考虑线段树二分,在查询 [SL,x]sum 时遍历到了节点 [l,r]

  1. rL
    • mxk 则直接结算,返回 sum
    • 否则 若 l=r,说明此节点是 SL,返回 1
    • 否则 若右子树的 mx>k,则遍历右子树并返回查询结果,否则遍历左子树并返回 查询结果 + 右子树的 sum
  2. 否则 当 右子树与 [SL,x] 无交时,遍历左子树并返回查询结果。
  3. 否则 先遍历右子树,然后查询右子树与 [SL,x] 的交区间的 mx,若 mxk,则继续遍历左子树,否则直接返回。

查询 (x,SR) 时类似,但注意当 l=r 时应返回 0

Code#

Copy
#include<bits/stdc++.h> using namespace std; #define int long long inline int read() { short f=1; int x=0; char c=getchar(); while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();} while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int q,k,a[200010],x[200010],len; bool op[200010],vis[200010]; set<int>s; set<int>::iterator pre,nxt; struct zzn { int l,r,mx,sum; #define ls (p<<1) #define rs (p<<1|1) #define mid (t[p].l+t[p].r>>1) }t[800010]; inline void upd(int p) { t[p].mx=max(t[ls].mx,t[rs].mx); t[p].sum=t[ls].sum+t[rs].sum; } void build(int p,int l,int r) { t[p].l=l;t[p].r=r; if(l==r) return; build(ls,l,mid);build(rs,-~mid,r); } void change(int p,int x,int d) { if(t[p].l==t[p].r) { t[p].mx=d;t[p].sum=1; return; } if(mid>=x) change(ls,x,d); else change(rs,x,d); upd(p); } void del(int p,int x) { if(t[p].l==t[p].r) { t[p].mx=t[p].sum=0; return; } if(mid>=x) del(ls,x); else del(rs,x); upd(p); } int query(int p,int l,int r) { if(t[p].l>=l&&t[p].r<=r) return t[p].mx; int res=0; if(mid>=l) res=max(res,query(ls,l,r)); if(mid<r) res=max(res,query(rs,l,r)); return res; } int query1(int p,int r) { if(t[p].r<=r) { if(t[p].mx<=k) return t[p].sum; if(t[p].l==t[p].r) return 1; return t[rs].mx<=k?t[rs].sum+query1(ls,r):query1(rs,r); } int res=0; if(mid<r) { res+=query1(rs,r); if(query(rs,t[rs].l,r)<=k) res+=query1(ls,r); } else res+=query1(ls,r); return res; } int query2(int p,int l) { if(t[p].l>=l) { if(t[p].mx<=k) return t[p].sum; if(t[p].l==t[p].r) return 0; return t[ls].mx<=k?t[ls].sum+query2(rs,l):query2(ls,l); } int res=0; if(mid>=l) { res+=query2(ls,l); if(query(ls,l,t[ls].r)<=k) res+=query2(rs,l); } else res+=query2(rs,l); return res; } signed main() { q=read();k=read(); for(int i=1;i<=q;i=-~i) op[i]=read()-1,a[i]=x[i]=read(); sort(a+1,a+q+1); len=unique(a+1,a+q+1)-a-1; build(1,1,len); for(int i=1;i<=q;i=-~i) { x[i]=lower_bound(a+1,a+len+1,x[i])-a; if(op[i]) printf("%d\n",query1(1,x[i])+(x[i]==len?0:query2(1,x[i]+1))); else { s.insert(x[i]); auto p=s.find(x[i]); if(p!=s.begin()) {pre=--p;++p;} nxt=++p;--p; if(vis[x[i]]) { del(1,x[i]);vis[x[i]]=0; if(nxt!=s.end()) change(1,*nxt,p!=s.begin()?a[*nxt]-a[*pre]:0);//若x是第一个元素则单点修改为0,保证其< s.erase(p); } else { vis[x[i]]=1; change(1,x[i],p!=s.begin()?a[x[i]]-a[*pre]:0); if(nxt!=s.end()) change(1,*nxt,a[*nxt]-a[x[i]]); } } } return 0; }
posted @   Sorato  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示
目录