[BZOJ 4569][SCOI 2016] 萌萌哒 区间并查集(ST表思想)

题目传送门:【BZOJ 4569】


题目大意: 有一个长度为 n 的大数。告诉你一些限制条件,每个条件表示为四个数,L1,R1,L2,R2,即两个长度相同的区间,表示区间内对应的数相同。比如 n=6 时,某限制条件 L1=1,R1=3,L2=4,R2=6,那么 123123,351351 这两组数均满足条件,但是 12012,131141 这两组数不满足条件,前者数的长度不为 6,后者第二位与第五位不同。现在,请你求出,满足以上所有条件的数有多少个。


题目分析:

在分析之前,先 Orz gengchen的题解 给我带来的启发

(这道题他讲得更详细,代码也更好懂)


进入正题

由题,首先看到这道题,由于题目性质为“对应的数位相等”,并且当数位之间有重合的时候,就会有对应的每个数都相等的情况(其实这样说是不准确的,但是,这些数至少都是相同的几组数),所以我们可以考虑使用并查集做法。

但是,朴素的并查集在这里会造成极大的浪费,很多情况被重复计算。所以,我们需要更好地优化并查集。在这里,我们可以使用最优的 ST 表优化方式(即:利用倍增的思想,对每个区块的数进行优化)。

具体的操作,可以看下面的代码,或者去上面看更详细的题解:

[cpp] view plain copy
print ?
  1. #include  
  2. #include  
  3. typedef long long LL;  
  4. const int MX=100005;  
  5. const int MXLOG=20;  
  6. const int MOD=998244353;  
  7.   
  8. int n,m,tot=0,ans=1;  
  9. int f[MXLOG][MX],log[MX*MXLOG],fa[MX*MXLOG],left[MX*MXLOG],right[MX*MXLOG];  
  10. //为了使用倍增而多开了log(n)的空间   
  11.   
  12. LL fastpow(int a,int b,int mod){  
  13.     LL r=1,base=a;  
  14.     while (b){  
  15.         if (b&1) r=r*base%mod;  
  16.         base=base*base%mod;  
  17.         b>>=1;  
  18.     }  
  19.     return r%mod;  
  20. }  
  21. void preprocess(int n){  
  22.     for (int i=1,j=0;i<=n;i<<=1,j++)log[i]=j;  
  23.     for (int i=1;i<=n;i++)log[i]=std::max(log[i-1],log[i]);  
  24.     for (int i=0;i<=log[n];i++){  
  25.         for (int j=1;j+(1<
  26.             f[i][j]=++tot;  
  27.             if (i>0){  
  28.                 //运用了ST表中的倍增思想   
  29.                 left[tot]=f[i-1][j];  
  30.                 right[tot]=f[i-1][j+(1<<(i-1))];  
  31.             }  
  32.         }  
  33.     }  
  34. }  
  35. inline int find(int x){  
  36.     if (fa[x]==x) return x;  
  37.     return fa[x]=find(fa[x]);  
  38. }  
  39. void merge(int lf,int rt){  
  40.     int x=find(lf),y=find(rt);  
  41.     if (x!=y) fa[x]=y;  
  42. }  
  43.   
  44. int main(){  
  45.     int Li,Ri,li,ri;  
  46.     scanf(”%d%d”,&n,&m);  
  47.     if (n==1){  
  48.         printf(”10”);  
  49.         return 0;  
  50.     }  
  51.     preprocess(n);  
  52.     for (int i=1;i<=tot;i++) fa[i]=i;  
  53.       
  54.     for (int i=1;i<=m;i++){  
  55.         scanf(”%d%d%d%d”,&Li,&Ri,&li,&ri);  
  56.         if (Li==li) continue;  
  57.         int size=log[Ri-Li+1];  
  58.         merge(f[size][Li],f[size][li]);  
  59.         merge(f[size][Ri-(1<
  60.     }  
  61.       
  62.     for (int i=tot;i>n;i–){  
  63.         int j=find(i);  
  64.         if (i!=j){  
  65.             //对应位置合并   
  66.             merge(left[i],left[j]);  
  67.             merge(right[i],right[j]);  
  68.         }  
  69.     }  
  70.       
  71.     for (int i=1;i<=n;i++)  
  72.         if (find(i)==i) ans=10LL*ans%MOD;  
  73.           
  74.     printf(”%lld”,9LL*ans%MOD*fastpow(10,MOD-2,MOD)%MOD);  
  75.     return 0;  
  76. }  

你可能感兴趣的:(各大OJ专题(POJ,BZOJ,hdu等),并查集,ST表(RMQ等))