码迷,mamicode.com
首页 > 其他好文 > 详细

[bzoj4569][SCOI2016]萌萌哒

时间:2016-05-07 10:44:30      阅读:143      评论:0      收藏:0      [点我收藏+]

标签:

题目大意

有一个无前导0的n位数,有m个限制形如[l1,r1]=[l2,r2],问满足条件的数有多少种,答案模10^9+7。

倍增

我们用ST表,f[i,j]表示[i,i+2^j-1]这一段。
那么初始时每一段单独成一个集合。
对于一个限制可以拆成log 份,然后进行集合合并。
然后呢,如果任意f[s,t]和f[i,j]属于同一集合,那么f[s,t-1]与f[i,j-1]以及f[s+2^(t-1)-1,t-1]和f[i+2^(j-1)-1,j-1]都应该属于同一集合。
为了满足这个限制,只要最后再一层一层的做,把下一层的合并了即可。
合并注意必须让编号大的合进编号小的里。
统计答案就很简单啦,具体看代码。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int maxn=100000+10,mo=1000000007,maxd=18;
int fa[maxn*21],id[maxn][21],di[maxn*21][2],two[21];
int i,j,k,l1,r1,l2,r2,s,t,n,m,ans,tot;
int getfa(int x){
    return fa[x]?fa[x]=getfa(fa[x]):x;
}
int merge(int x,int y){
    x=getfa(x);
    y=getfa(y);
    if (x>y) swap(x,y);
    if (x!=y) fa[y]=x;
}
int main(){
    two[0]=1;
    fo(i,1,maxd) two[i]=two[i-1]*2;
    scanf("%d%d",&n,&m);
    if (n==1){
        printf("10\n");
        return 0;
    }
    fo(i,1,n)
        fo(j,0,maxd)
            id[i][j]=++tot,di[tot][0]=i,di[tot][1]=j;
    fo(i,1,m){
        scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
        fd(j,maxd,0)
            if (l1+two[j]-1<=r1){
                merge(id[l1][j],id[l2][j]);
                l1+=two[j];l2+=two[j];
            }
    }
    fd(j,maxd,1)
        fo(i,1,n){
            k=getfa(id[i][j]);
            s=di[k][0];t=di[k][1];
            merge(id[s][t-1],id[i][j-1]);
            merge(id[s+two[t-1]][t-1],id[i+two[j-1]][j-1]);
        }
    ans=1;
    fo(i,1,n)
        if (!fa[id[i][0]])
            ans=(ll)ans*((i==1)?9:10)%mo;
    printf("%d\n",ans);
}

[bzoj4569][SCOI2016]萌萌哒

标签:

原文地址:http://blog.csdn.net/werkeytom_ftd/article/details/51334030

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!