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?+1?Sl1?+2??Sr1??与Sl2Sl2+1Sl2+2?Sr2S_{l_2}S_{l_2+1}S_{l_2+2} \cdots S_{r_2}Sl2??Sl2?+1?Sl2?+2??Sr2??完全相同。

比如 n=6n=6n=6 时,某限制条件 l1=1,r1=3,l2=4,r2=6l_1=1,r_1=3,l_2=4,r_2=6l1?=1,r1?=3,l2?=4,r2?=6 ,那么 123123123123123123,351351351351351351 均满足条件,但是 120121201212012,131141131141131141 不满足条件,前者数的长度不为 666 ,后者第二位与第五位不同。问满足以上所有条件的数有多少个。

输入格式

第一行两个数n和m,分别表示大数的长度,以及限制条件的个数。

接下来m行,对于第i行,有4个数 li1,ri1,li2,ri2,分别表示该限制条件对应的两个区间。

1≤n≤1051\le n\le 10^51≤n≤105,1≤m≤1051\le m\le 10^51≤m≤105 ,1≤li1,ri1,li2,ri2≤n 1\le li1,ri1,li2,ri2 \le n 1≤li1,ri1,li2,ri2≤n ;并且保证 ri1?li1=ri2?li2 ri1-li1=ri2-li2 ri1?li1=ri2?li2 。

输出格式

一个数,表示满足所有条件且长度为n的大数的个数,答案可能很大,因此输出答案模 109+7 10^9+7 109+7 的结果即可。


#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int _=1e5+10,N=16,p=1e9+7,mod=1e9+7;
#define int long long
int n,m,f[22][_];
int get(int x,int y){
    return f[y][x]==x?x:f[y][x]=get(f[y][x],y);
}
bool vis[_];
inline int ksm(int x,int y){
    int res=1;
    while(y){
        if(y&1)res=res*x%mod;
        x=x*x%mod; y>>=1;
    }
    return res;
}
void merge(int x,int y,int len){
  if(get(x,len)!=get(y,len))
    f[len][f[len][x]]=f[len][y];
}
signed main(){
    cin>>n>>m;
    for(int j=0;j<=21;j++)for(int i=1;i<=n;i++)f[j][i]=i;
    int l1,l2,r1,r2;
    for(int i=1;i<=m;i++){
        scanf("%lld%lld%lld%lld",&l1,&r1,&l2,&r2);
        for(int j=20;j>=0;j--)
        if(l1+(1<<j)-1<=r1)merge(l1,l2,j),l1+=1<<j,l2+=1<<j;
    }
    int ans=9,cnt=0;
    for(int j=20;j;j--)
    for(int i=1;i+(1<<j)-1<=n;i++)
    {merge(i,get(i,j),j-1);merge(i+(1<<(j-1)),f[j][i]+(1<<(j-1)),j-1);}
    for(int i=1;i<=n;i++)if(get(i,0)==i)cnt++;
    for(int i=1;i<cnt;i++)ans*=10,ans%=mod;
    cout<<ans<<endl;
}

原文地址:https://www.cnblogs.com/naruto-mzx/p/12194446.html

时间: 2024-08-29 19:48:55

luogu P3295 [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]萌萌哒(并查集+倍增)

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]萌萌哒

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

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

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

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

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

[luogu P2170] 选学霸(并查集+dp)

题目传送门:https://www.luogu.org/problem/show?pid=2170 题目描述 老师想从N名学生中选M人当学霸,但有K对人实力相当,如果实力相当的人中,一部分被选上,另一部分没有,同学们就会抗议.所以老师想请你帮他求出他该选多少学霸,才能既不让同学们抗议,又与原来的M尽可能接近 输入输出格式 输入格式: 第一行,三个正整数N,M,K. 第2...K行,每行2个数,表示一对实力相当的人的编号(编号为1-N) 输出格式: 一行,表示既不让同学们抗议,又与原来的M尽可能接

BZOJ 1370: [Baltic2003]Gang团伙(luogu 1892)(种类并查集)

题面: bzoj题面有误,还是看luogu的吧 https://www.luogu.org/problemnew/show/P1892 题解: 种类并查集.. 因为有敌人的敌人是朋友这个条件,所以需要一个中转点.. 因此,将每个点拆成两个点,一个是朋友点,另一个是敌人点.当读到A与B是朋友时,就将A与B所对应的朋友点并集:当读到两个点是敌人的时候,就将A点所对应的敌人点与B所对应的朋友点并集,将A所对应的朋友点和B所对应的敌人点并集. 代码: #include<bits/stdc++.h> u

luogu 3295[SCOI2016]萌萌哒

题目链接:luogu 3295 很明显的想法是用并查集维护最后有几个块内的数字必须相等,设最后有\(cnt\)个联通块,那么答案就是\(9\times10^{cnt-1}\) 暴力维护并查集是\(O(nlen)\)的,显然超时,考虑优化 记\(f_{i,j}\)表示区间\([i,i+2^j-1]\)的编号,编号相同则区间中元素值相同 考虑将一段区间\((l_1,r_1)\)和\((l_2,r_2)\)合并时,将每个区间拆成\(log\)段,合并\((l_1,l_1+2^k-1)\)和\((l_2

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