ABC356F 題解

Sorato發表於2024-06-25

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]\)

  1. \(r\leq L\)
    • \(mx\leq k\) 則直接結算,返回 \(sum\)
    • 否則 若 \(l=r\),說明此節點是 \(S_L\),返回 \(1\)
    • 否則 若右子樹的 \(mx>k\),則遍歷右子樹並返回查詢結果,否則遍歷左子樹並返回 查詢結果 + 右子樹的 \(sum\)
  2. 否則 當 右子樹與 \([S_L,x]\) 無交時,遍歷左子樹並返回查詢結果。
  3. 否則 先遍歷右子樹,然後查詢右子樹與 \([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;
}