zoukankan      html  css  js  c++  java
  • [SCOI2016]萌萌哒 解题报告

    [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;
    }
    
  • 相关阅读:
    使用git bash提交代码到github托管
    电子邮件的正则表达式
    PHP正则表达式及实例
    php中session_start()函数的作用
    mysql 中文乱码
    mysql 安装以及配置
    高质量JAVA代码编写规范
    DAO设计模式
    深入浅出UML类图
    分析业务模型-类图(Class Diagram)
  • 原文地址:https://www.cnblogs.com/BruceW/p/12194438.html
Copyright © 2011-2022 走看看