ABC356F Distance Component Size Query 题解
ABC356F Distance Component Size Query
题目大意#
有 次操作和一个无向图 ,以及一个常数 。操作有两种:
- 给定 ,若 则从 中删去 ,否则将 插入 ,并将 与所有 连边。
- 给定 ,求 中 所在连通块的大小。
Solve#
若用一个有序集合 维护 中的点,并对其作差分,令差分后的集合为 ,每次操作 2 相当于查询 中 左侧第一个 的数的位置 ,以及 右侧第一个 的数的位置 ,答案即为 ( 不计入区间, 计入)。
如何维护 。
考虑把操作离线,离散化所有 。然后以离散化后的 为下标建立线段树,值为 中 对应的差值。即:若 中 位于 ,则线段树上以 为下标的值为 。
对于线段树上的每个节点 ,我们再记录 为 上的最大值, 为 上有多少下标已被插入 。
修改:
- 对于插入操作:先将 插入 ,再查询 中 的位置 ,单点修改 为 , 为 。
- 对于删除操作:查询 中 的位置 ,单点修改 为 。
查询:
考虑线段树二分,在查询 的 时遍历到了节点 :
- 当 时
- 若 则直接结算,返回 。
- 否则 若 ,说明此节点是 ,返回 。
- 否则 若右子树的 ,则遍历右子树并返回查询结果,否则遍历左子树并返回 查询结果 + 右子树的 。
- 否则 当 右子树与 无交时,遍历左子树并返回查询结果。
- 否则 先遍历右子树,然后查询右子树与 的交区间的 ,若 ,则继续遍历左子树,否则直接返回。
查询 时类似,但注意当 时应返回 。
Code#
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】