[BZOJ4569] [Luogu 3295] [SCOI2016]萌萌哒(并查集+倍增)
[BZOJ4569] [Luogu 3295] [SCOI2016]萌萌哒(并查集+倍增)
题面
有一个n位的十进制数a(无前导0),给出m条限制,每条限制\((l_1,r_1,l_2,r_2)(保证r_1-l_1=r_2-l_2)\)表示这个数的第\([l_1,r_1]\)位与\([l_2,r_2]\)位相同。问有多少个这样的数满足条件,答案取模\(10^9+7\),
\(n \leq 10^5\)
分析
约定:我们把十进制数a的每一位从高到低称为第1位,第2位...,记为\(a_i\)
首先很妙的一点是,把区间限制转化成元素相等,即\(a_{l_1+k}=a_{r_1+k}(k \in [0,r_1-l_1+1])\)。然后把所有相等的元素看作一个联通块,对于每条限制暴力合并,最后我们就会得到\(cnt\)个联通块。对于每个联通块,我们要给它的所有元素赋一个相同的值。包含\(a_1\)的联通块里的元素只能在\([1,9]\)内取值(因为第一位不能是0),其他联通块的元素可以在\([0,9]\)内取值。根据乘法原理,答案就是\(9 \times 10^{cnt-1}\)
那么问题就是如何快速维护连通性。直接做是\(O(n^2\alpha(n))\)的。注意到我们一个个合并\([l_1,r_1]\)内的所有元素,很浪费时间,可以用倍增优化。
我们将并查集的1维改成2维,\(f[i][j]\)表示区间\([j,j+2^i-1]\)的代表元。合并的时候我们只需要把\(r_1-l_1+1\)二进制拆分,如13拆成\(2^3+2^2+2^0\).对于每一位\(j\),合并\(f[j]\)上对应的一段长度为\(2^j\)的区间。
int len=r1-l1+1;
for(int j=log2n;j>=0;j--){//二进制拆分
if(len&(1<<j)){
S.merge(j,l1,l2);
l1+=(1<<j);
l2+=(1<<j);
}
}

[图片来自mhy博客]
如下图,3的二进制拆分为\(2^1+2^0\)先合并长度为\(2^1\)的区间,再合并长度为\(2^0\)的区间
但是我们查询的时候需要查询的是每个点的信息,即\(f[0][i]\),所以我们要下推合并。合并长度为\(2^j\),起点分别为\(x,y\)的区间时,我们把区间断成两半,合并\(f[j-1][x+2^{j-1}],f[j-1][y+2^{j-1}]\),还要合并\(f[j-1][x],f[j-1][y]\)这样做\(O(\log n)\)次,就可以得到\(f[0][i]\)
for(int j=log2n;j>=1;j--){
for(int i=1;i+(1<<j)-1<=n;i++){//下推关系,长度为2^j的推到2^(j-1)
int f=S.find(j,i);
S.merge(j-1,i,f);
S.merge(j-1,i+(1<<(j-1)),f+(1<<(j-1)));
}
}
时间复杂度为\(O(n \alpha(n)\log n )\),但是这里我偷了个懒,只写了路径压缩的并查集,时间复杂度\(O(n \log^2 n)\),由于常数很小,可以通过。
代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#define maxn 100000
#define maxlogn 21
#define mod 1000000007
using namespace std;
typedef long long ll;
inline ll fast_pow(ll x,ll k){
ll ans=1;
while(k){
if(k&1) ans=ans*x%mod;
x=x*x%mod;
k>>=1;
}
return ans;
}
struct disjoint_set{
int fa[maxlogn+5][maxn+5];//f[i][j]表示[j,j+2^i-1]的代表元
void ini(int n){
int k=log2(n)+1;
for(int j=0;j<=k;j++){
for(int i=1;i<=n;i++){
fa[j][i]=i;
}
}
}
int find(int b,int x){
if(fa[b][x]==x) return x;
else return fa[b][x]=find(b,fa[b][x]);
}
void merge(int b,int x,int y){
int fx=find(b,x);
int fy=find(b,y);
if(fx!=fy) fa[b][fx]=fy;
}
}S;
int log2n;
int n,m;
int main(){
int l1,l2,r1,r2;
scanf("%d %d",&n,&m);
log2n=log2(n)+1;
S.ini(n);
for(int i=1;i<=m;i++){
scanf("%d %d %d %d",&l1,&r1,&l2,&r2);
int len=r1-l1+1;
for(int j=log2n;j>=0;j--){//二进制拆分
if(len&(1<<j)){
S.merge(j,l1,l2);
l1+=(1<<j);
l2+=(1<<j);
}
}
}
for(int j=log2n;j>=1;j--){
for(int i=1;i+(1<<j)-1<=n;i++){//下推关系,长度为2^j的推到2^(j-1)
int f=S.find(j,i);
S.merge(j-1,i,f);
S.merge(j-1,i+(1<<(j-1)),f+(1<<(j-1)));
}
}
int cnt=0;
for(int i=1;i<=n;i++){
if(S.find(0,i)==i) cnt++;
}
printf("%lld\n",9*fast_pow(10,cnt-1)%mod);
}
[BZOJ4569] [Luogu 3295] [SCOI2016]萌萌哒(并查集+倍增)的更多相关文章
- Luogu P3295 [SCOI2016]萌萌哒(并查集+倍增)
P3295 [SCOI2016]萌萌哒 题面 题目描述 一个长度为 \(n\) 的大数,用 \(S_1S_2S_3 \cdots S_n\) 表示,其中 \(S_i\) 表示数的第 \(i\) 位, ...
- 洛谷 3295 [SCOI2016]萌萌哒——并查集优化连边
题目:https://www.luogu.org/problemnew/show/P3295 当要连的边形如 “一段区间内都是 i 向 i+L 连边” 的时候,用并查集优化连边. 在连边的时候,如果要 ...
- [bzoj4569][SCOI2016]萌萌哒-并查集+倍增
Brief Description 一个长度为n的大数,用S1S2S3...Sn表示,其中Si表示数的第i位,S1是数的最高位,告诉你一些限制条件,每个条 件表示为四个数,l1,r1,l2,r2,即两 ...
- BZOJ 4569: [Scoi2016]萌萌哒 [并查集 倍增]
传送门 题意:长为$n \le 10^5$的数字,给出$m \le 10^5$个限制$[l1,r1]\ [l2,r2]$两个子串完全相等,求方案数 把所有要求相等的位置连起来,不就是$9*10^{连通 ...
- LOJ2014 SCOI2016 萌萌哒 并查集、ST表优化连边
传送门 一个朴素的做法就是暴力连边并查集,可是这是\(O(n^2)\)的.发现每一次连边可以看成两个区间覆盖,这两个区间之间一一对应地连边.可线段树对应的两个节点的size可能不同,这会导致" ...
- bzoj 4569 [Scoi2016]萌萌哒 并查集 + ST表
题目链接 Description 一个长度为\(n\)的大数,用\(S_1S_2S_3...S_n\)表示,其中\(S_i\)表示数的第\(i\)位,\(S_1\)是数的最高位,告诉你一些限制条件,每 ...
- BZOJ4569 [SCOI2016]萌萌哒 【并查集 + 倍增】
题目链接 BZOJ4569 题解 倍增的思想很棒 题目实际上就是每次让我们合并两个区间对应位置的数,最后的答案\(ans = 9 \times 10^{tot - 1}\),\(tot\)是联通块数, ...
- 2018.07.31 bzoj4569: [Scoi2016]萌萌哒(并查集+倍增)
传送门 对于每个限制,使用倍增的二进制拆分思想,用并查集数组fa[i][j]" role="presentation" style="position: rel ...
- luogu P3295 [SCOI2016]萌萌哒
传送门 题目条件"两个子串\(S[l_1,r_1],S[l_2,r_2]\)完全相同"等价于\(\forall i \in[0,r_1-l_1+1],S_{l1+i}=S_{l_2 ...
随机推荐
- 019:re_path函数详解
re_path使用: 1.re_path和path的作用是一样的,只不过re_path在写url的时候可以使用正则表达式——功能更加强大: 2.使用正则表达式时,推荐使用原生字符串(即:已 r 开头的 ...
- 12. ClustrixDB 为容错和可用性分配磁盘空间
集群必须包含足够的空闲磁盘空间,以便从节点或区域故障中自动恢复.要计算在发生故障后仍然允许ClustrixDB完全重新保护数据的情况下可以使用的最大磁盘空间量,可以使用以下公式: 最大磁盘利用率% = ...
- MoreExecutors工具类使用
MoreExecutors是guava提供的工具类,是对jdk自带的Executors工具类的扩展,主要方法如下: 1.addDelayedShutDown()方法的两个重载: public stat ...
- asp.net+批量下载附件
asp.net 下载文件几种方式 protected void Button1_Click(object sender, EventArgs e) { /* 微软为Response对象提供了一个新的方 ...
- 病毒 x
04:病毒 查看 提交 统计 提问 总时间限制: 1000ms 内存限制: 65535kB 描述 有一天,小y突然发现自己的计算机感染了一种病毒!还好,小y发现这种病毒很弱,只是会把文档中的所有字 ...
- springboot(五).如何在springboot项目中使用拦截器
在每个项目中,拦截器都是我们经常会去使用的东西,基本上任一一个项目都缺不了拦截器的使用. 如日志记录.登录验证,session验证等,都需要拦截器来拦截URL请求,那springboot中的拦截器是如 ...
- springMVC的常用注解有哪些?
1.@Controller @Controller 用于标记在一个类上,使用它标记的类就是一个SpringMVC Controller 对象.分发处理器将会扫描使用了该注解的类的方法,并检测该方法是否 ...
- 从三十而立的迷茫,到30K
很标题党的标题,但是希望分享一下自己的经历,一份有点儿不寻常的经历. 12年创业,到17年末,举步维艰,没有项目,公司只剩下两个人,负债,现实给我这个每见过什么市面,却自命不凡的人狠狠的一个耳光.加上 ...
- webpack配置之webpack.config.js文件配置
webpack配置之webpack.config.js文件配置 webpack.config.js webpack resolve 1.总是手动的输入webpack的输入输出文件路径,是一件非常繁琐 ...
- LeetCode 300——最长上升子序列
1. 题目 2. 解答 2.1. 动态规划 我们定义状态 state[i] 表示以 nums[i] 为结尾元素的最长上升子序列的长度,那么状态转移方程为: \[state[i] = max(state ...