洛谷P3295 [SCOI2016]萌萌哒

题目描述:

 

 题意:给你一个长度和多个限制,限制指给定的两个区间内的值必须相等,问一共有几种情况。

哪里萌萌哒了

思路分析:我们能想到的最暴力的思路应该就是用并查集了,每给定两个区间,我们就把这两个区间上相应的点都并在一起,来表示它们的值相等,最后统计一下一共有多少个并查集就行了,答案就是9*10^(n-1)(n是并查集数量),因为第一个数不能为0,所以只有九种情况。

但这样是一定会超时的,于是我们来想办法进行优化,看到区间,你想到了什么?ST表?线段树?对了,这道题我们就可以用ST表的思路来进行优化,我们在进行并查集时可以用p[i][j]表示以i为起点的点,向后2^j的长度的区间,这样在合并时就不用再一个点一个点的合并了,就相当于对区间进行了二进制拆分。但是我们这样并不能解决输出的问题,因为此时每个点都被绑在区间上,没有自己的并查集,于是我们就需要将每个点再和它区间的祖先进行合并,这样之后就可以再统计并查集的数目进行计算输出了。其他细节详见注释。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 typedef long long ll;
 5 const int N=1e5+10;
 6 const int Max=21;
 7 const int Mod=1000000007;
 8 int fa[N][Max];
 9 int find(int x,int y){//查找p[x][y]的祖先 
10     if(fa[x][y]==x) return x;
11     return fa[x][y]=find(fa[x][y],y);
12 }
13 void merge(int x,int y,int i){//合并 
14     x=find(x,i);y=find(y,i);
15     if(x!=y){
16         fa[x][i]=y;
17     }
18 }
19 int main(){
20     int n,m;
21     scanf("%d%d",&n,&m);
22     for(int i=1;i<=n;++i)  //并查集初始化 
23         for(int j=0;j<Max;++j)
24             fa[i][j]=i;
25     for(int i=1;i<=m;++i){
26         int l1,l2,r1,r2;
27         scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
28         for(int j=Max-1;~j;--j){//对拆出来的每一段进行合并 
29             if(l1+(1<<j)-1<=r1){
30                 merge(l1,l2,j);
31                 l1+=(1<<j);l2+=(1<<j);//走下一段 
32             }
33         }
34     }
35     for(int j=Max-1;j;--j){  //将祖先分到每个区间的节点上 
36         for(int i=1;i+(1<<j)-1<=n;++i){
37             merge(i,find(i,j),j-1);
38             merge(i+(1<<j-1),find(i,j)+(1<<j-1),j-1);
39         }
40     }
41     int num=0;
42     ll ans=9;
43     for(int i=1;i<=n;++i) //计算答案 
44         if(find(i,0)==i) num++;
45     for(int i=1;i<num;++i){
46         ans=(ans*10)%Mod;
47     }
48     printf("%lld\n",ans);
49     return 0;
50 }
View Code

 

posted @ 2020-05-07 12:07  19502-李嘉豪  阅读(125)  评论(0编辑  收藏  举报