zoukankan      html  css  js  c++  java
  • [SCOI2016]萌萌哒(倍增+并查集)

    一个长度为n的大数,用S1S2S3...Sn表示,其中Si表示数的第i位,S1是数的最高位,告诉你一些限制条件,每个条件表示为四个数,l1,r1,l2,r2,即两个长度相同的区间,表示子串Sl1Sl1+1Sl1+2...Sr1与Sl2Sl2+1Sl2+2...Sr2完全相同。比如n=6时,某限制条件l1=1,r1=3,l2=4,r2=6,那么123123,351351均满足条件,但是12012,131141不满足条件,前者数的长度不为6,后者第二位与第五位不同。问满足以上所有条件的数有多少个。

    Solution

    涨姿势了。

    不难想到用并查集维护数字之间的相等关系,最后用联通块个数统计答案。

    但这样的复杂度是n^2的,需要去优化它,

    考虑到每次合并都是两段等长的区间进行合并,所以我们考虑使用倍增。

    我们开nlogn个并查集,num[i][j]表示从i开始的2^j个数,每次区间合并我们把它拆成logn个区间分别合并。

    最后自顶向底合并儿子,就像线段树一样,

    Code

    #include<iostream>
    #include<cstdio>
    #define N 100002
    using namespace std;
    typedef long long ll;
    const int mod=1e9+7;
    int num[N][20],f[N*20],n,m,tot,son[N*20][2],l1,r1,l2,r2;
    int find(int x){return f[x]=f[x]==x?x:find(f[x]);}
    long long power(ll x,int y){
        ll ans=1;
        while(y){
            if(y&1)(ans*=x)%=mod;
            (x*=x)%=mod;
            y>>=1;
        }
        return ans;
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=0;(1<<i)<=n;++i)
          for(int j=1;j+(1<<i)-1<=n;++j){
            num[j][i]=++tot;f[tot]=tot;
            if(i){
            son[tot][0]=num[j][i-1];
            son[tot][1]=num[j+(1<<i-1)][i-1];
            }
           }
        for(int i=1;i<=m;++i){
            scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
            int len=r1-l1+1;
            for(int j=19;j>=0;--j)
             if((1<<j)<=len){
                   int x=find(num[l1][j]),y=find(num[l2][j]);
                   if(x!=y)f[x]=y; 
                   len-=(1<<j);l1+=(1<<j);l2+=(1<<j);
             }
        } 
        for(int i=19;i>=0;--i)
          for(int j=1;j+(1<<i)-1<=n;++j){
              int root=num[j][i];
              if(find(root)!=root){
                 int x=find(son[root][0]),y=find(son[f[root]][0]);
               if(x!=y)f[x]=y;    
               x=find(son[root][1]),y=find(son[f[root]][1]);
               if(x!=y)f[x]=y;
            }
        }
        int ans=0;
        for(int i=1;i<=n;++i)if(find(num[i][0])==num[i][0])ans++;   
        printf("%lld",9*power(10,ans-1)%mod);
        return 0;
    }
  • 相关阅读:
    oracle 处理找被删掉且提交了事务的数据
    java去除下划线并首字母大写
    假数据仓库-常见数据枚举(日期、月份、周几、星期几,前导零、Excel 列号)
    自然语言处理标注工具——Brat(安装、测试、使用)
    判断当前点击位置在不在某个区域内
    java调用C#程序集
    UE使用EditorUtilityWidget完成简单的编辑器内工具
    CodeForces 230B
    mac中安装启动使用jmeter步骤
    Ubuntu中samba配置过程
  • 原文地址:https://www.cnblogs.com/ZH-comld/p/9816703.html
Copyright © 2011-2022 走看看