ABC356F Distance Component Size Query 题解
ABC356F Distance Component Size Query
题目大意
有 \(q\) 次操作和一个无向图 \(G\),以及一个常数 \(k\)。操作有两种:
- 给定 \(x\),若 \(x\in G\) 则从 \(G\) 中删去 \(x\),否则将 \(x\) 插入 \(G\),并将 \(x\) 与所有 \(y\in[x-k,x+k]\cap G\) 连边。
- 给定 \(x\),求 \(G\) 中 \(x\) 所在连通块的大小。
Solve
若用一个有序集合 \(S\) 维护 \(G\) 中的点,并对其作差分,令差分后的集合为 \(C\),每次操作 2 相当于查询 \(C\) 中 \(x\) 左侧第一个 \(>k\) 的数的位置 \(L\),以及 \(x\) 右侧第一个 \(>k\) 的数的位置 \(R\),答案即为 \(R-L\)(\(R\) 不计入区间,\(L\) 计入)。
如何维护 \(S\)。
考虑把操作离线,离散化所有 \(x\)。然后以离散化后的 \(x\) 为下标建立线段树,值为 \(C\) 中 \(x\) 对应的差值。即:若 \(S\) 中 \(x\) 位于 \(p\),则线段树上以 \(x\) 为下标的值为 \(C_p\)。
对于线段树上的每个节点 \([l,r]\),我们再记录 \(mx\) 为 \([l,r]\) 上的最大值,\(sum\) 为 \([l,r]\) 上有多少下标已被插入 \(S\)。
修改:
- 对于插入操作:先将 \(x\) 插入 \(S\),再查询 \(S\) 中 \(x\) 的位置 \(p\),单点修改 \(x\) 为 \(S_p-S_{p-1}\),\(S_{p+1}\) 为 \({S_{p+1}-x}\)。
- 对于删除操作:查询 \(S\) 中 \(x\) 的位置 \(p\),单点修改 \(S_{p+1}\) 为 \(S_{p+1}-S_{p-1}\)。
查询:
考虑线段树二分,在查询 \([S_L,x]\) 的 \(sum\) 时遍历到了节点 \([l,r]\):
- 当 \(r\leq L\) 时
- 若 \(mx\leq k\) 则直接结算,返回 \(sum\)。
- 否则 若 \(l=r\),说明此节点是 \(S_L\),返回 \(1\)。
- 否则 若右子树的 \(mx>k\),则遍历右子树并返回查询结果,否则遍历左子树并返回 查询结果 + 右子树的 \(sum\)。
- 否则 当 右子树与 \([S_L,x]\) 无交时,遍历左子树并返回查询结果。
- 否则 先遍历右子树,然后查询右子树与 \([S_L,x]\) 的交区间的 \(mx'\),若 \(mx'\leq k\),则继续遍历左子树,否则直接返回。
查询 \((x,S_R)\) 时类似,但注意当 \(l=r\) 时应返回 \(0\)。
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;
}