[SCOI2016]萌萌哒 解题报告
Posted brucew-07
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[SCOI2016]萌萌哒 解题报告相关的知识,希望对你有一定的参考价值。
题意
有一个长度为 (n) 的大数,
有 (m) 个形如 l1,r1,l2,r2
的限制, 表示区间 ([l1,r1]) 和 ([l2,r2]) 完全相等,
求满足这些限制的数的个数, 不能含有前导零.
((1 le n,m le 10^5))
思路
暴力 : 直接 (O(nm)) 把相等的数合成一个并查集, 最后若并查集的数量为 (k), 则答案为 (9 imes 10^{k-1}). (不能有前导零).
考虑优化.
首先想到线段树优化连边, 但是发现用线段树的话, 无法确定区间的对应关系.
为了连边之后依旧能确定区间的对应关系, 我们需要对每一个点都有一个以它为起点的存储结构.
所以, 我们可以弄一个类似于 ST表 的东西, 但理解方式类似于 线段树.
把整个序列分成 (log n) 层, 每一层之间进行连边.
在给定了 l1,r1,l2,r2
时, 用类似于ST表查询最值时的操作, 把 ([l1,r1]) 和 ([l2,r2]) 分别分成两个长度为 (2) 的若干次方的区间, 然后在这几个区间之间连边.
在所有边都连完后, 从上往下一层一层地把连边下传, 反映在代码中其实就是并查集的合并.
需要注意的是, 在下传的时候, 设当前层的两个连接区间为 (A,B), 我们还需要考虑 (A) 和 (B) 的子区间, 即下一层的四个区间 (A_1,A_2) 和 (B_1,B_2) 的连接情况,
若 (A_1,A_2) 是相连的, 就把 (fa[B_1],fa[B_2]) 设为 (fa[A_1]),
否则, 就把 (fa[A_1],fa[A_2]) 设为 (fa[B_1]).
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int _=1e5+7;
const int L=20;
const ll mod=1e9+7;
int n,m,fa[_][L+7],Log[_];
bool vis[_];
ll ans;
void init(){
cin>>n>>m;
int t=0;
for(int i=1;i<=n;i++){
if(i==1<<(t+1)) t++;
Log[i]=t;
for(int k=0;k<=L;k++)
fa[i][k]=i;
}
}
int find(int i,int k){ return fa[i][k]==i ?i :fa[i][k]=find(fa[i][k],k); }
void run(){
int l1,r1,l2,r2;
int maxn=0;
for(int i=1;i<=m;i++){
scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
maxn=max(maxn,max(r1,r2));
int t=Log[r1-l1+1];
int f1=find(l1,t),f2=find(l2,t);
fa[f1][t]=f2;
f1=find(r1-(1<<t)+1,t),f2=find(r2-(1<<t)+1,t);
fa[f1][t]=f2;
}
for(int k=L;k>=1;k--){
for(int i=1;i+(1<<k)-1<=n;i++){
int t=find(i,k),t1=find(i,k-1),t2=find(i+(1<<(k-1)),k-1);
if(t1==t2){ fa[find(t,k-1)][k-1]=fa[find(t+(1<<(k-1)),k-1)][k-1]=t1; }
else{ fa[t1][k-1]=find(t,k-1); fa[t2][k-1]=find(t+(1<<(k-1)),k-1); }
}
}
int j=find(1,0);
ans=9;
for(int i=2;i<=n;i++)
if(i!=j&&find(i,0)==i)
ans=ans*10%mod;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("x.in","r",stdin);
#endif
init();
run();
printf("%lld
",ans);
return 0;
}
以上是关于[SCOI2016]萌萌哒 解题报告的主要内容,如果未能解决你的问题,请参考以下文章