p3295 [SCOI2016]萌萌哒

传送门

分析

我们可以将一个点拆成logN个点,分别代表从点i开始,长度为2^k的子串 那么当我们处理两个区间相等的关系时,对区间做二进制拆分,拆成log个区间,分别并起来即可

当然我们这样做修改是省心了,但是同时查询的时候也会带来一些麻烦……因为,我们要求的信息是最底层的,只能是长度为1的区间,而不能有奇奇怪怪的区间 不过没关系,我们这时运用等式1,拆分并查集

具体来讲,我们从最长的区间开始逐个枚举,每次查找他和他的父亲,然后把它和父亲都劈成两半,前一半和前一半连边,后一半和后一半连边即可,这样相当于把较长区间并查集拆成两个一半的并查集

最后我们就有了一些关于那些点相等的信息,直接计算并查集个数即可

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<ctime>
#include<vector>
#include<set>
#include<map>
#include<stack>
using namespace std;
const int mod = 1e9+7;
const int LOG = 20;
int fa[LOG+2][100010],n,m,l1,l2,r1,r2,cnt;
long long Ans=9;
inline int sf(int x,int y){return fa[y][x]==x?x:fa[y][x]=sf(fa[y][x],y);}
inline void mer(int x,int y,int k){if(sf(x,k)!=sf(y,k))fa[k][sf(x,k)]=sf(y,k);}
int main(){
    int i,j,k;
    scanf("%d%d",&n,&m);
    for(i=0;i<=LOG;i++)
      for(j=1;j<=n;j++)fa[i][j]=j;
    for(i=1;i<=m;i++){
      scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
      for(j=LOG;j>=0;j--)
        if(l1+(1<<j)-1<=r1){
          mer(l1,l2,j);
          l1+=(1<<j);
          l2+=(1<<j);
        }
    }
    for(i=LOG;i>0;i--)
      for(j=1;j+(1<<i)-1<=n;j++){
        mer(j,sf(j,i),i-1);
        mer(j+(1<<(i-1)),fa[i][j]+(1<<(i-1)),i-1);
      }
    for(i=1;i<=n;i++)
      if(sf(i,0)==i)cnt++;
    for(i=1;i<cnt;i++)Ans=Ans*10%mod;
    cout<<Ans;
    return 0;
}

原文地址:https://www.cnblogs.com/yzxverygood/p/10354458.html

时间: 2024-10-29 21:46:48

p3295 [SCOI2016]萌萌哒的相关文章

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} \

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

Luogu P3295 [SCOI2016]萌萌哒

Link 一个很trivial的想法是并查集暴力维护,最后的答案是\(9*10^n\),\(n\)是最后剩下的并查集的个数. 因为每次连边的都是一段区间到另一端区间,所以我们可以二进制拆分,把并查集的每个点拆成\(\log\)个点,每次把要连边的区间拆成\(\log\)个点直接连,最后再还原. #include<cstdio> #include<cctype> #include<numeric> #include<algorithm> using std::

【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不满足条件,前者数

bzoj 4569: [Scoi2016]萌萌哒

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

4569: [Scoi2016]萌萌哒

4569: [Scoi2016]萌萌哒 链接 分析: 每次给出的两个区间长度是一样的,对应位置的数字也是一样的,那么可以将两两对应的数字用并查集合并,设最后有$cnt$个不同的集合,答案就是$9\times 10 ^{cnt-1}$,第一个数不能是0. 暴力合并太慢了,考虑优化.对于一段区间,用倍增的思想分成log段,分别合并log段,最后的下放一下标记即可.类似线段树的懒标记. 代码: #include<cstdio> #include<algorithm> #include&l

SCOI2016萌萌哒题解

SCOI2016萌萌哒题解 题目链接 思路 这个题目大概就是给你一些限制,使区间相等,那么一个很巧妙的思路就是将区间限制转化为两个店在一个并查集里 我们要求的是只有一位的并查集的个数,答案为 ( t我们要求的是只有一位的并查集的个数) 但直接操作复杂度过大 于是有人想到了倍增 f[i][j]表示区间[ , ]所在的并查集 最开始时记录每个大区间的左右儿子 输入 l1 l2 r1 r2时像ST表一样合并 再分别处理每个大区间,如果f[i][j]和f[a][b]在一个并查集里,那么他们的左右儿子也在

[SCOI2016]萌萌哒 解题报告

[SCOI2016]萌萌哒 题意 有一个长度为 \(n\) 的大数, 有 \(m\) 个形如 l1,r1,l2,r2的限制, 表示区间 \([l1,r1]\) 和 \([l2,r2]\) 完全相等, 求满足这些限制的数的个数, 不能含有前导零. \((1 \le n,m \le 10^5)\) 思路 暴力 : 直接 \(O(nm)\) 把相等的数合成一个并查集, 最后若并查集的数量为 \(k\), 则答案为 \(9 \times 10^{k-1}\). (不能有前导零). 考虑优化. 首先想到线

BZOJ4569 : [Scoi2016]萌萌哒

建立ST表,每层维护一个并查集. 每个信息可以拆成两条长度为$2$的幂次的区间相等的信息,等价于ST表里两对点的合并. 然后递归合并,一旦发现已经合并过了就退出. 因为一共只会发生$O(n\log n)$次合并,所以时间复杂度为$O(n\log n\alpha(n))$. #include<cstdio> int n,m,i,j,a,b,c,d,f[17][100010],v[100010],ans=9; int F(int i,int j){return f[i][j]==j?j:f[i][