zoukankan      html  css  js  c++  java
  • 【BZOJ-4569】萌萌哒 ST表 + 并查集

    4569: [Scoi2016]萌萌哒

    Time Limit: 10 Sec  Memory Limit: 256 MB
    Submit: 459  Solved: 209
    [Submit][Status][Discuss]

    Description

    一个长度为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,后者第二位与第五位不同。问满足以上所有条件的数有多少个。

    Input

    第一行两个数n和m,分别表示大数的长度,以及限制条件的个数。接下来m行,对于第i行,有4个数li1,ri1,li2,ri2,分别表示该限制条件对应的两个区间。
    1≤n≤10^5,1≤m≤10^5,1≤li1,ri1,li2,ri2≤n;并且保证ri1-li1=ri2-li2。

    Output

     一个数,表示满足所有条件且长度为n的大数的个数,答案可能很大,因此输出答案模10^9+7的结果即可。

    Sample Input

    4 2
    1 2 3 4
    3 3 3 3

    Sample Output

    90

    HINT

    Source

    Solution

    这道题非常巧妙

    先思考暴力,对每一位维护一个并查集,每次限制就是把那两个区间信息合并,最后答案根据剩有的计算即可

    那么问题在于如何快速的合并

    考虑线段树,分出来的区间过多,合并还是有问题,所以换种方法

    倍增!建立ST表,相当于对同层的建出一棵类似树的东西,每层维护并查集,相当于把信息拆成$2^{?}$的两段

    合并优先合并大的,这样一共是$nlogn$段,最多合并$nlogn$次

    递归合并,发现合并过就可以跳出了,总的答案统计一下用快速幂计算一下即可

    特判N=1

    总的时间复杂度$O(nlogn*a(n))$

    启发:倍增并不仅仅应用于LCA或SA求LCP之类的,应该灵活运用这种思想

    Code

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    using namespace std;
    void Freopen() {freopen("game.in","r",stdin); freopen("game.out","w",stdout);}
    #define P 1000000007
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
        while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    int N,M;
    long long Quick_Pow(long long x,long long y)
    {
        long long re=1;
        for (int i=y; i; i>>=1,x=x*x%P)
            if (i&1) re=re*x%P;
        return re;
    }
    #define MAXN 100010
    int fa[MAXN][21];
    int Find(int x,int y) {if (fa[x][y]==x) return x; else return fa[x][y]=Find(fa[x][y],y);}
    void Merge(int x1,int x2,int k)
    {
        int f1=Find(x1,k),f2=Find(x2,k);
        if (f1==f2) return; 
        fa[f1][k]=f2;
        if (k--) {Merge(x1,x2,k),Merge(x1+(1<<k),x2+(1<<k),k);}
    }
    int Len;
    int main()
    {
        Freopen();
        N=read(),M=read();
        for (int i=1; i<=N; i++)
            for (int j=0; j<=17; j++)
                fa[i][j]=i;
        while (M--)
            {
                int l1=read(),r1=read(),l2=read(),r2=read();
                int k=0; while ((1<<k)<=r1-l1+1) k++; k--;
                Merge(l1,l2,k); Merge(r1-(1<<k)+1,r2-(1<<k)+1,k);
            }
        for (int i=1; i<=N; i++) if (fa[i][0]==i) Len++;
        printf("%d
    ",N==1? 10:int(9LL*Quick_Pow(10,Len-1)%P));
        return 0;
    } 
  • 相关阅读:
    迭代器简介
    关于判断对象是否相等的问题
    NIO
    BIO流分类介绍
    servlet简介
    http协议简介
    爬虫常用链接
    http和https协议
    爬虫的合法性研究
    爬虫介绍
  • 原文地址:https://www.cnblogs.com/DaD3zZ-Beyonder/p/5734233.html
Copyright © 2011-2022 走看看