zoukankan      html  css  js  c++  java
  • 【BZOJ4569】[Scoi2016]萌萌哒 倍增+并查集

    【BZOJ4569】[Scoi2016]萌萌哒

    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

    题解:看到这种题比较直接的思路就是用并查集。对于每个限制,就将所有对应的位置所在并查集合并,设最后联通块个数为cnt,答案就是$9 imes 10^{cnt-1}$。

    然后比较直接的想法就是用线段树优化这个过程,不过想了半天,线段树好像。。。做不到啊。

    看题解发现是倍增,如何实现呢?每次限制,我们相当于将log对区间的并查集合并(每对区间的大小相等)。最后也像线段树那样,pushdown一下。即如果两个区间在同一个并查集中,那么他们的左半部分和右半部分都分别在相同的并查集中。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #define P(A,B) ((A)*n+(B))
    using namespace std;
    const int maxn=100010;
    const long long mod=1000000007;
    int n,m,sum;
    long long ans;
    int f[20][maxn],Log[maxn];
    int find(int x,int y)
    {
    	return (f[y][x]==x)?x:(f[y][x]=find(f[y][x],y));
    }
    int rd()
    {
    	int ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
    	return ret*f;
    }
    void updata(int a,int b,int c)
    {
    	if(find(a,c)!=find(b,c))	f[c][f[c][a]]=f[c][b];
    }
    int main()
    {
    	n=rd(),m=rd();
    	int i,j,a,b,c,d;
    	for(i=2;i<=n;i++)	Log[i]=Log[i>>1]+1;
    	for(j=0;(1<<j)<=n;j++)	for(i=1;i+(1<<j)-1<=n;i++)	f[j][i]=i;
    	for(i=1;i<=m;i++)
    	{
    		a=rd(),b=rd(),c=rd(),d=rd();
    		for(j=Log[b-a+1];j>=0;j--)	if(a+(1<<j)-1<=b)
    			updata(a,c,j),a+=(1<<j),c+=(1<<j);
    	}
    	for(j=Log[n];j;j--)
    	{
    		for(i=1;i+(1<<j)-1<=n;i++)	updata(i,find(i,j),j-1),updata(i+(1<<j-1),f[j][i]+(1<<j-1),j-1);
    	}
    	for(i=1;i<=n;i++)	if(find(i,0)==i)	sum++;
    	for(ans=9,i=1;i<sum;i++)	ans=ans*10%mod;
    	printf("%lld",ans);
    	return 0;
    }
  • 相关阅读:
    leetcode目录
    Windows下tuxedo配置
    实习总结
    n人比赛,可轮空,比赛轮数和场数
    Ubuntu中Eclipse安装与配置
    Lunix中文乱码解决方案
    tuxedo入门
    useradd和adduser的区别
    每个位上都是素数
    TUXEDO错误解决方案
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7189654.html
Copyright © 2011-2022 走看看