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;
}