BZOJ4569: [Scoi2016]萌萌哒

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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值