SCOI2016萌萌哒题解

SCOI2016萌萌哒题解

题目链接


 

思路

这个题目大概就是给你一些限制,使区间相等,那么一个很巧妙的思路就是将区间限制转化为两个店在一个并查集里

我们要求的是只有一位的并查集的个数,答案为 
( t我们要求的是只有一位的并查集的个数)

但直接操作复杂度过大

于是有人想到了倍增

f[i][j]表示区间[ , ]所在的并查集

最开始时记录每个大区间的左右儿子

输入 l1 l2 r1 r2时像ST表一样合并

 

再分别处理每个大区间,如果f[i][j]f[a][b]在一个并查集里,那么他们的左右儿子也在一个并查集里,我们就能将大区间合并信息下传为左右儿子的合并(左儿子与左儿子合并,右儿子与右儿子合并)

最后统计f[i][0]的并查集个数即可

 

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define re register int
#define ll long long
#define MN 1000050
#define mod 1000000007
using namespace std;
int bcj[MN*22],f[MN][22],lc[MN*22],rc[MN*22],lg[MN];
int n,m,k,ltk,cnt;
int find(int x){
    if(x==bcj[x])return x;
    bcj[x]=find(bcj[x]);
    return bcj[x];
}
void merge(int x,int y){
    int t1=find(x),t2=find(y);
    if(t1!=t2)bcj[t2]=t1;
}
int main(){
    scanf("%d%d",&n,&m);
    for(re i=2;i<=n;i++)
        lg[i]=lg[i/2]+1;    
    for(re j=lg[n];j>=0;j--)
        for(re i=1;i+(1<<j)-1<=n;i++){
                f[i][j]=++cnt;
                bcj[cnt]=cnt;
        }//给大区间编号
    for(re j=1;j<=lg[n];j++)
        for(re i=1;i+(1<<j)-1<=n;i++){
            lc[f[i][j]]=f[i][j-1];
            rc[f[i][j]]=f[i+(1<<(j-1))][j-1];
        }//统计左右儿子
    for(re i=1;i<=m;i++){
        int a1,a2,a3,a4;
        scanf("%d%d%d%d",&a1,&a2,&a3,&a4);
        int l=lg[a2-a1+1];
        merge(f[a1][l],f[a3][l]);
        merge(f[a2-(1<<l)+1][l],f[a4-(1<<l)+1][l]);
    }//像st表一样合并
    for(re i=1;i<=cnt;i++){
        if(!lc[i]||!rc[i])continue;
        int t1=find(i);
        if(t1!=i){
            merge(lc[t1],lc[i]);
            merge(rc[t1],rc[i]);
            //如果f[i][j]与f[a][b]同属一个联通块,那么他们左右儿子也同属一个联通块
        }
    }
    ltk=n;
    //先假设每个位置都是独立的联通块,如果联通再减
    for(re i=1;i<=n;i++){
        if(find(f[i][0])!=f[i][0])
            ltk--;
    }
    ll ans=9;
    for(re i=1;i<ltk;i++)
        ans=(ans%mod*10)%mod;
        printf("%lld",ans%mod);
    return 0;
}

 

posted @ 2019-07-31 10:15  红色OI再临  阅读(111)  评论(0编辑  收藏  举报