CF763E-题解
1.基本思路
观察到题目反复询问连续区间的一种难以统计的权值,时间为
同时,题目询问的问题和联通块数量有关,考虑使用并查集维护。
想要同时处理点增加和删除是很困难的,尝试使用回滚莫队,单独对增加操作进行处理。
总体时间复杂度
2.具体细节
由于我们有
接下来我们再仔细看一下回滚莫队的细节。
利用可撤销并查集暴力合并,然后暴力撤销,单次时间复杂度
对块外的部分先用并查集扩展,然后对块内部分进行扩展,统计答案后对块内部分撤销。
除此之外,要注意到我们新增点时加边不要超出询问
代码:
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N=500005;
int n,k,m,q,Ans[N],cnn,nowr;
vector<int> tol[N],tor[N];
struct query{int l,r,id;}Q[N];
int pos[N],_R[N],BAS;
int prt[N],siz[N];
stack< pair<int,int> > s;
int fa(int s){return prt[s]==s?s:fa(prt[s]);}
int merge(int x,int y)
{
x=fa(x),y=fa(y);
if(x==y) return 0;
if(siz[x]>siz[y]) swap(x,y);
prt[x]=y,siz[y]+=siz[x];
s.push({x,y});
return 1;
}
void del(int tot=s.size())
{
while(tot--)
{
int x=s.top().first,y=s.top().second; s.pop();
prt[x]=x,siz[y]-=siz[x];
}
}
int add(int u,int lim,int tp)
{
++cnn;
int rec=0;
for(auto v:tp?tor[u]:tol[u])
if((tp?v<=lim:v>=lim)) rec+=merge(u,v);
cnn-=rec;
return rec;
}
int main()
{
scanf("%d %d %d",&n,&k,&m);
BAS=(int)sqrt(n);
for(int i=1;i<=n;++i)
prt[i]=i,siz[i]=1,pos[i]=(i-1)/BAS+1,_R[pos[i]]=i;
for(int i=1,x,y;i<=m;++i)
{
scanf("%d %d",&x,&y);
if(x>y) swap(x,y);
tor[x].push_back(y),tol[y].push_back(x);
}
scanf("%d",&q);
for(int i=1;i<=q;++i) scanf("%d %d",&Q[i].l,&Q[i].r),Q[i].id=i;
sort(Q+1,Q+q+1,[](query a,query b){return pos[a.l]==pos[b.l]?a.r<b.r:pos[a.l]<pos[b.l];});
for(int i=1;i<=q;++i)
{
int l=Q[i].l,r=Q[i].r;
if(pos[l]!=pos[Q[i-1].l] || (pos[Q[i-1].l]==pos[Q[i-1].r]&&pos[l]!=pos[r]))
del(),cnn=0,nowr=_R[pos[l]];
if(pos[l]==pos[r])
{
cnn=0;
for(int j=l,tmp;j<=r;++j) tmp=add(j,l,0);
Ans[Q[i].id]=cnn;
del();
}
else
{
int tmp=0,ctt=0;
while(nowr+1<=r) tmp=add(++nowr,_R[pos[l]]+1,0);
int tmpcnn=cnn;
for(int j=_R[pos[l]];j>=l;--j) ctt+=add(j,nowr,1);
Ans[Q[i].id]=cnn;
del(ctt),cnn=tmpcnn;
}
}
for(int i=1;i<=q;++i) printf("%d\n",Ans[i]);
}