题目链接
分析
话说今天的两道题好像都是SCOI的?
题意就是给定多个限制条件,让你求满足条件的数。
第一眼看成了数位DP,发现不可行,因为可能每个位置都要枚举一遍,于是就会发现,其实每个位置0-9都可以放,除了首位,还有每个限制位置的数字应该相等,不难想到并查集+乘法计数原理。
初始的时候,每个位置都是一个单独的集合,每次加限制条件的时候把区间内对应的点合并就行,这样只需要统计出集合的个数(n),然后最后答案就是(9*10^{n-1}),9是首位不能放0去掉了一个数字,关键是这样做会T掉。
要怎么优化呢?这个我的确也没想到,去百度了一下竟然用的是倍增?
这道题怎么用倍增的思想呢?和稀疏表一样,这样就相当于合并了整段区间,这样就省去了很多遍历的时间。
但问题就回来了,合并大区间的时候是方便了,我们查询的时候怎么办,查询的时候不可能去考虑每个大区间,而是单个的点,那么我们就需要考虑将每个大区间的合并下放到小区间。
其实也跟稀疏表那个一样,分成两个区间就行了,然后把这个区间上的点都合并到大区间的根上,然后就完了。
感觉这个题自己想,可能真的够呛,因为这个倍增实在是太女少口阿了。
#include<cstdio>
#define ll long long
const int N=1e5+10,Mod=1e9+7;
int f[N][25];
int find(int x,int y){
return f[x][y]==x?x:(f[x][y]=find(f[x][y],y));
}
void Mer(int x,int y,int l){
f[find(x,l)][l]=find(y,l);
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int j=0;j<=20;j++)
for(int i=1;i<=n;i++)
f[i][j]=i;
for(int i=1;i<=m;i++){
int a,b,c,d;
scanf("%d%d%d%d",&a,&b,&c,&d);
for(int j=20;j>=0;j--)
if(a+(1<<j)-1<=b)
Mer(a,c,j),a+=1<<j,c+=1<<j;
}
for(int j=20;j;j--)
for(int i=1;i+(1<<j)-1<=n;i++)
Mer(i,find(i,j),j-1),Mer(i+(1<<j-1),f[i][j]+(1<<j-1),j-1);
int cnt=0;
ll ans=9;
for(int i=1;i<=n;i++)
if(find(i,0)==i)cnt++;
for(int i=1;i<cnt;i++){
ans*=10;
ans%=Mod;
}
printf("%lld
",ans);
}