BZOJ4569: [Scoi2016]萌萌哒

来源:互联网 发布:波形发生电路实验数据 编辑:程序博客网 时间:2024/05/24 23:12

BZOJ4569: [Scoi2016]萌萌哒

并查集·倍增

题解:

并查集中点id[i][j]表示从i开始2j长度的这一块区间。
合并的时候区间拆成不超过log个2的整数次幂长度的区间,把他们对应的点合并。
最后自顶向下合并,即如果id[i][j]]id[a][b]在一个并查集里,则合并id[i][j1]id[a][b1]以及id[i+2j1][j1]id[a+2b1][b1].
答案数一数最底层有几个连通块就可以算出来。

Code:

#include <iostream>#include <cstring>#include <cstdio>using namespace std;const int N = 100005;const long long mod = 1000000007;const int LOG = 20;int n,m,tot;int pa[N*21],base[N*21],log[N*21],pow[21],id[N][21];int find(int x){ return pa[x]?pa[x]=find(pa[x]):x; }void un(int x,int y){    int xx=find(x), yy=find(y);    if(xx!=yy) pa[xx]=yy;}void init(){    for(int i=0;i<=LOG;i++) pow[i]=1<<i;    for(int i=1;i<=n;i++){        for(int j=0;j<=LOG;j++){            id[i][j]=++tot;            base[tot]=i; log[tot]=j;        }    }}int main(){    freopen("a.in","r",stdin);    scanf("%d%d",&n,&m);    init();    while(m--){        int a,b,c,d;        scanf("%d%d%d%d",&a,&b,&c,&d);        for(int j=LOG;j>=0;j--){            if(a+pow[j]-1 <= b){                un(id[a][j],id[c][j]);                a+=pow[j]; c+=pow[j];            }        }    }    for(int j=LOG;j>0;j--){        for(int i=1;i<=n;i++){            if(i+pow[j]-1 > n) break;            int x=find(id[i][j]);            int a=base[x], b=log[x];            un(id[a][b-1],id[i][j-1]);            un(id[a+pow[b-1]][b-1],id[i+pow[j-1]][j-1]);        }    }    long long ans=9; bool flag=false;    for(int i=1;i<=n;i++){        if(!pa[id[i][0]]){            if(flag) ans=ans*10%mod;            flag=true;        }    }    printf("%lld\n",ans);}