bzoj4569 [Scoi2016]萌萌哒(并查集+倍增)

考虑暴力做法,就是每次把对应位置都用并查集合并了。最后答案就是9*10^(连通块个数-1)。然而这样太暴力了,怎么优化呢?
我们考虑倍增的思想,用id[j][i]这个点表示j…j+(1< < i)-1这一段。我们每次合并两个对应区间时,变成合并logn个对应区间。
最后再从上往下推到i=0.即如果id[x][i]和id[y][i]联通,则id[x][i-1],id[y][i-1]也联通, id[x][x+(1<<i1)],id[y][y+(1<<i1)] 也联通。
复杂度 O((n+m)lognα(nlogn))

#include 
#include 
#include 
#include 
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 100010
#define mod 1000000007
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int n,m,Log[N],id[N][17],tot=0,fa[N*17],toid[N*17];
inline int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
inline void merge(int x,int y){
    int xx=find(x),yy=find(y);
    if(xx!=yy) fa[xx]=yy;
}
inline void gao(int x,int y,int len){
    for(int i=0;i<=Log[len];++i)
        if(len>>i&1) merge(id[x][i],id[y][i]),x+=(1<1<int main(){
//  freopen("a.in","r",stdin);
    n=read();m=read();Log[0]=-1;
    for(int i=1;i<=n;++i) Log[i]=Log[i>>1]+1;
    for(int i=0;i<=Log[n];++i)
        for(int j=1;j<=n;++j){
            if(j+(1<1>n) break;id[j][i]=++tot;toid[tot]=j;
        }
    for(int i=1;i<=tot;++i) fa[i]=i;
    while(m--){
        int x1=read(),y1=read(),x2=read(),y2=read();
        gao(x1,x2,y1-x1+1);
    }for(int i=Log[n];i>=1;--i)
        for(int j=1;j<=n;++j){
            if(j+(1<1>n) break;int x=id[j][i],xx=find(x);if(x==xx) continue;
            xx=toid[xx];x=j;merge(id[x][i-1],id[xx][i-1]);
            x+=(1<1);xx+=(1<1);merge(id[x][i-1],id[xx][i-1]);
        }tot=0;int ans=9;
    for(int i=1;i<=n;++i) if(find(id[i][0])==id[i][0]) ++tot;
    for(int i=1;i10%mod;
    printf("%d\n",ans);
    return 0;
}

你可能感兴趣的:(st表,并查集,bzoj)