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

当区间\([a,b]\)和\([c,d]\)对应相等时。
我们把两个区间对应位置上的数所在并查集合并。
最后并查集的数量为\(num\)答案就是\(9*10^num\)因为是个数,不能有前置\(0\)。
但是两个区间对应位置上的数所在并查集合并太浪费时间。
怎么办。
考虑使用倍增。
我们用\((i,j)\)代表\([i,i+(1<<j)-1]\)这个区间然后任何一个区间最多可以\(log\)个这样的倍增的区间拼起来。
然后呢?
我们按倍增区间的大小从大往小枚举。当\((x,i)\)和\((y,i)\)在一个并查集里时,\((x,i-1)\)和\((y,i-1)\)在一个并查集里。\((x+(1<<i-1),i-1)\)和\((y+(1<<i-1),i-1)\)也在一个并查集里。我们把这两对倍增区间所在并查集合并,最后长度为\(1\)的元素所在并查集也被合并。复杂度为\(O(nlogn)\)。
吐槽一波题目

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define int long long
const int mod=1e9+7;
const int N=101000;
int fa[N*22],id[N][22],pw[22],n,m,ans,book[N],tot,xb[N*22],l[N*22];
int find(int x){
    if(fa[x]==x)return x;
    else return fa[x]=find(fa[x]);
}
int ksm(int x,int b){
    int tmp=1;
    while(b){
        if(b&1)tmp=tmp*x%mod;
        b>>=1;
        x=x*x%mod;
    }
    return tmp;
}
int read(){
    int sum=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    return sum*f;
}
void pre_work(int x){
    pw[0]=1;
    for(int i=1;i<=20;i++)pw[i]=pw[i-1]*2;
    int len=log2(x);
    for(int j=0;j<=len;j++)
        for(int i=1;i+pw[j]-1<=n;i++)
            id[i][j]=++tot,xb[tot]=i,l[tot]=j,fa[tot]=tot;
}
signed main(){
    n=read();m=read();
    pre_work(n);
    while(m--){
        int a=read(),b=read();
        int c=read(),d=read();
        for(int i=20;i>=0;i--)
            if(a+pw[i]-1<=b){
                int x=find(id[a][i]),y=find(id[c][i]);
                if(x!=y)fa[x]=y;
                a=a+pw[i];c=c+pw[i];
            }
    }
    int len=log2(n);
    for(int j=len;j>=1;j--)
        for(int i=1;i+pw[j]-1<=n;i++){
            int f=find(id[i][j]);
            int x=find(id[i][j-1]);
            int y=find(id[xb[f]][l[f]-1]);
            if(x!=y)fa[x]=y;
            x=find(id[i+pw[j-1]][j-1]);
            y=find(id[xb[f]+pw[l[f]-1]][l[f]-1]);
            if(x!=y)fa[x]=y;
        }
    for(int i=1;i<=n;i++){
        int x=find(id[i][0]);
        if(book[x]==0)ans++,book[x]=1;
    }
    printf("%lld",9ll*ksm(10,ans-1)%mod);
    return 0;
}

原文地址:https://www.cnblogs.com/Xu-daxia/p/10520086.html

时间: 2024-10-06 10:39:26

[SCOI2016]萌萌哒(倍增+并查集)的相关文章

【BZOJ4569】[Scoi2016]萌萌哒 倍增+并查集

[BZOJ4569][Scoi2016]萌萌哒 Description 一个长度为n的大数,用S1S2S3...Sn表示,其中Si表示数的第i位,S1是数的最高位,告诉你一些限制条件,每个条件表示为四个数,l1,r1,l2,r2,即两个长度相同的区间,表示子串Sl1Sl1+1Sl1+2...Sr1与Sl2Sl2+1Sl2+2...Sr2完全相同.比如n=6时,某限制条件l1=1,r1=3,l2=4,r2=6,那么123123,351351均满足条件,但是12012,131141不满足条件,前者数

luogu P3295 [SCOI2016]萌萌哒 |倍增+并查集

题目描述 一个长度为 nnn 的大数,用 S1S2S3?SnS_1S_2S_3 \cdots S_nS1?S2?S3??Sn?表示,其中 SiS_iSi? 表示数的第 iii 位, S1S_1S1? 是数的最高位.告诉你一些限制条件,每个条件表示为四个数,l1,r1,l2,r2l_1,r_1,l_2,r_2l1?,r1?,l2?,r2?,即两个长度相同的区间,表示子串Sl1Sl1+1Sl1+2?Sr1S_{l_1}S_{l_1+1}S_{l_1+2} \cdots S_{r_1}Sl1??Sl1

【BZOJ4569】萌萌哒(并查集,倍增)

[BZOJ4569]萌萌哒(并查集,倍增) 题面 BZOJ 题意: 有一个长度为\(n\)的数 给定\(m\)个限制条件 每次限制\(l1-r1\)与\(l2-r2\)是相同的 求出方案数 题解 如果每次给定的限制都是告诉你某一位和某一位是相同的 那么,我们的做法是: 并查集,然后计算有\(k\)个联通块 \(ans=9*10^{k-1}\) 但是,现在每次给定的都是一个区间 我们不太可能暴力的把区间之间的位置两两进行一次合并 所以,我们来想个办法优化一下. 试试倍增? 维护\(logn\)个并

Codeforces 827D Best Edge Weight 倍增 + 并查集 || 倍增 + 压倍增标记 (看题解)

Best Edge Weight 我们先找出一棵最小生成树, 对于非树边来说, 答案就是两点路径上的最大值 - 1, 这个直接倍增就能处理. 对于树边来说, 就是非树边的路径经过这条边的最小值 - 1, 这个可以用并查集压缩路径 或者 更压st表一样的方式更新. 感觉就是没想到先扣出来一个最小生成树, 而是往克鲁斯卡尔的过程中想了. #include<bits/stdc++.h> #define LL long long #define LD long double #define ull u

Luogu P3295 [SCOI2016]萌萌哒(并查集+倍增)

P3295 [SCOI2016]萌萌哒 题面 题目描述 一个长度为 \(n\) 的大数,用 \(S_1S_2S_3 \cdots S_n\) 表示,其中 \(S_i\) 表示数的第 \(i\) 位, \(S_1\) 是数的最高位.告诉你一些限制条件,每个条件表示为四个数, \(l_1,r_1,l_2,r_2\) ,即两个长度相同的区间,表示子串 \(S_{l_1}S_{l_1+1}S_{l_1+2} \cdots S_{r_1}\) 与 \(S_{l_2}S_{l_2+1}S_{l_2+2} \

【BZOJ-4569】萌萌哒 ST表 + 并查集

4569: [Scoi2016]萌萌哒 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 459  Solved: 209[Submit][Status][Discuss] Description 一个长度为n的大数,用S1S2S3...Sn表示,其中Si表示数的第i位,S1是数的最高位,告诉你一些限制条件,每个条件表示为四个数,l1,r1,l2,r2,即两个长度相同的区间,表示子串Sl1Sl1+1Sl1+2...Sr1与Sl2Sl2+1Sl2+2.

四川oi 萌萌哒 (分层并查集)

萌萌哒 时间限制: 1 Sec  内存限制: 256 MB提交: 12  解决: 2[提交][状态][讨论版] 题目描述 一个长度为 n 的大数,用 S1S2S3...Sn表示,其中 Si表示数的第 i 位, S1是数的最高位,告诉你一些限制条件,每个条件表示为四个数,l1, r1, l2, r2,即两个长度相同的区间,表示子串 Sl1Sl1+1Sl1+2...Sr1与 Sl2Sl2+1Sl2+2...Sr2完全相同.比如 n = 6 时,某限制条件 l1= 1, r1= 3, l2 = 4,

hdu6109(并查集+set/倍增)

题目 http://acm.hdu.edu.cn/showproblem.php?pid=6109 分析 对于相同的条件,明显直接并查集 对于不同的条件,可以用set来保存,并查集合并的时候也要对set启发式合并 还有另一种很奇妙的做法 如果我们只考虑一段[l..r]是否可行,那么我们可以离线,先挑出相同的条件构出并查集,然后对所有的不同条件进行判断就行了 这样我们就可以写出一个时间复杂度为O(r-l)的check(l,r) 那我们怎么分段呢? 我们对于当前起点now,进行倍增寻找长度最小的L(

【CodeForces】827 D. Best Edge Weight 最小生成树+倍增LCA+并查集

[题意]给定n个点m条边的带边权无向连通图,对每条边求最大边权,满足其他边权不变的前提下图的任意最小生成树都经过它.n,m<=2*10^5,1<=wi<=10^9. [算法]最小生成树+倍增LCA+并查集 [题解]首先求出图的一个最小生成树,则所有边分成树边和非树边. 对于非树边(u,v),假设u和v在最小生成树上的路径的最大边权Max,那么一定满足w(u,v)<=Max /////////////////////////////////////// 原文地址:https://ww