题意
F.A.Qs | Home | Discuss | ProblemSet | Status | Ranklist | Contest | 入门OJ | ModifyUser autoint | Logout | 捐赠本站 |
---|
4569: [Scoi2016]萌萌哒
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 1619 Solved: 842
[ Submit][ Status][ Discuss]
Description
Input
Output
一个数,表示满足所有条件且长度为n的大数的个数,答案可能很大,因此输出答案模10^9+7的结果即可。
Sample Input
1 2 3 4
3 3 3 3
Sample Output
HINT
Source
HOME Back
分析
考试的时候只会暴力并查集。后来参照Mangoyang的题解。
观察发现,限制条件的本质是使得一些位置只能填同样的字符,我们不妨把限制在一起的位置看做点,在它们之间连一条边
那么统一联通块里面的位置就只能填同一字符了,所以设联通快数为 ,那么答案就是 (第一位不能填 ,所以少一种选择)
于是就有一个暴力的做法,用并查集维护联通块,每次对于一组限制 ,暴力将两个区间的对应点合并,复杂度
考虑怎么优化并查集的合并,由于是区间问题,所以很容易就想到用线段树或者 表来维护
每次把可以询问区间拆成 个区间,区间与区间之间进行连边,难点在于最后怎么将区间之间的合并转化到点上
由于题目只需要最终询问一次,不妨利用 的思想,对每一种长度的区间用一个并查集来维护,最后算答案的时候将合并信息下传
具体来讲,考虑 表的做法:设 表示左端点为 的长度为 的区间所在集合的 的左端点
那么下传信息的时候只需要 分别和 合并即可,最后统计一下 的联通块数 ,总复杂度
代码
#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
rg T data=0,w=1;
rg char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') w=-1;
ch=getchar();
}
while(isdigit(ch))
data=data*10+ch-'0',ch=getchar();
return data*w;
}
template<class T>il T read(rg T&x){
return x=read<T>();
}
typedef long long ll;
co int N=1e5+1,mod=1e9+7;
int fa[N][21],n,m;
int ask(int x,int y){
return x==fa[x][y]?x:fa[x][y]=ask(fa[x][y],y);
}
int main(){
// freopen(".in","r",stdin),freopen(".out","w",stdout);
read(n),read(m);
for(int i=1;i<=n;++i)
for(int j=0;j<=20;++j) fa[i][j]=i;
for(int l1,r1,l2,r2;m--;){
read(l1),read(r1),read(l2),read(r2);
int ps1=l1,ps2=l2;
for(int i=20;~i;--i) if(ps1+(1<<i)-1<=r1){
int p=ask(ps1,i),q=ask(ps2,i);
if(p!=q) fa[p][i]=q;
ps1+=(1<<i),ps2+=(1<<i);
}
}
for(int j=20;j;--j)
for(int i=1;i+(1<<j)-1<=n;++i){
int p=ask(i,j),q=ask(i,j-1);
fa[q][j-1]=ask(p,j-1);
p=ask(i,j),q=ask(i+(1<<j-1),j-1);
fa[q][j-1]=ask(p+(1<<j-1),j-1);
}
int ans=1;
for(int i=1;i<=n;++i) if(fa[i][0]==i)
ans=(ll)ans*(ans==1?9:10)%mod;
printf("%d\n",ans);
return 0;
}
观察发现,限制条件的本质是使得一些位置只能填同样的字符,我们不妨把限制在一起的位置看做点,在它们之间连一条边
那么统一联通块里面的位置就只能填同一字符了,所以设联通快数为 ,那么答案就是 (第一位不能填 ,所以少一种选择)
于是就有一个暴力的做法,用并查集维护联通块,每次对于一组限制 ,暴力将两个区间的对应点合并,复杂度
考虑怎么优化并查集的合并,由于是区间问题,所以很容易就想到用线段树或者 表来维护
每次把可以询问区间拆成 个区间,区间与区间之间进行连边,难点在于最后怎么将区间之间的合并转化到点上
由于题目只需要最终询问一次,不妨利用 的思想,对每一种长度的区间用一个并查集来维护,最后算答案的时候将合并信息下传
具体来讲,考虑 表的做法:设 表示左端点为 的长度为 的区间所在集合的 的左端点
那么下传信息的时候只需要 分别和 合并即可,最后统计一下 的联通块数 ,总复杂度