【Codeforces 763E】Timofey and our friends animals

回滚莫队,可撤销并查集。

题目大意:

给定 $n$ 个点,每个点只可能和其左右距离不超过 $k$ 的点有边相连。

有多组询问,每次给定 $l,r$,问只保留 $l\sim r$ 的点以及边的情况下,有多少连通块。

题解:

$10^5$ 还给了 $7$ 秒,那我应该想都不想直接莽回滚莫队才对啊……

好像有个非常短的做法能过但貌似是假的?

既然回滚莫队能跑那就写啊。

大概就是对序列分块,将左端点在一个块里的询问放到一起处理。

处理一个块的询问的时候,按照右端点排序,然后处理每个询问的时候,先移动右端点,并更新并查集。然后移动左端点到目标位置。

处理完一个询问后,立即将左端点的位置还原。使用可撤销并查集实现按顺序撤销的操作。

时间复杂度 $O(n\sqrt n\log n)$。

实际上好像最慢的点也才 $1\texttt s$。

Code:

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
#define bel(x)((x-1)/siz+1)
const int N=1e5+5,siz=400;
int fa[N],sz[N],ans[N],k,n,m,t;
bool Lf[N][5],Rg[N][5];
inline int Find(int x){while(x!=fa[x])x=fa[x]=fa[fa[x]];return x;}
inline int find(int x){while(x!=fa[x])x=fa[x];return x;}
struct que{
    int l,r,id;
    inline bool operator<(const que&rhs)const{return r<rhs.r;}
};
vector<que>vec[500];
int sta[N*10],top; 
void work(int l,int r,int id){
    int s=r-l+1;
    for(int i=l;i<=r;++i)
    for(int j=0;j<k&&i+j<r;++j)if(Rg[i][j]){
        int x=Find(i),y=Find(i+j+1);
        if(x!=y){
            --s;
            if(sz[x]<sz[y])fa[x]=y,sz[y]+=sz[x];else fa[y]=x,sz[x]+=sz[y];
        }
    }
    ans[id]=s;
    for(int i=l;i<=r;++i)fa[i]=i,sz[i]=1;
}
int main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;++i)fa[i]=i,sz[i]=1;
    scanf("%d",&t);
    while(t--){
        int u,v;
        scanf("%d%d",&u,&v);
        if(u>v)u^=v^=u^=v;
        Rg[u][v-u-1]=Lf[v][v-u-1]=1;
    }
    scanf("%d",&m);
    for(int i=1;i<=m;++i){
        int l,r;
        scanf("%d%d",&l,&r);
        if(r-l+1<=1000)work(l,r,i);else
        vec[bel(l)].push_back((que){l,r,i});
    }
    for(int V=1;V<400;++V)if(!vec[V].empty()){
        for(int i=1;i<=n;++i)fa[i]=i,sz[i]=1;
        sort(vec[V].begin(),vec[V].end());
        int r=V*siz,s=0;
        for(que q:vec[V]){
            while(r<q.r){
                ++r;
                for(int j=0;j<k&&r-j-1>=V*siz;++j)
                if(Lf[r][j]){
                    int x=find(r),y=find(r-j-1);
                    if(x!=y){
                        ++s;
                        if(sz[x]<sz[y])fa[x]=y,sz[y]+=sz[x];else fa[y]=x,sz[x]+=sz[y];
                    }
                }
            }
            int nw=s;top=0;
            for(int l=V*siz-1;l>=q.l;--l)
            for(int j=0;j<k;++j)if(Rg[l][j]){
                int x=find(l),y=find(l+j+1);
                if(x!=y){
                    ++s;
                    if(sz[x]<sz[y])swap(x,y);
                    sta[++top]=y,fa[y]=x,sz[x]+=sz[y];
                }
            }
            ans[q.id]=q.r-q.l+1-s,s=nw;
            for(int i=top;i;--i){
                int x=sta[i];
                sz[fa[x]]-=sz[x],fa[x]=x;
            }
        }
    }
    for(int i=1;i<=m;++i)printf("%d\n",ans[i]);
    return 0;
}