【JZOJ4807】破解

来源:互联网 发布:阿里云 域名解析到网址 编辑:程序博客网 时间:2024/05/01 00:27

Description

给出M个区间[Li,Ri],每次可以选择一个区间[s,t]把01串[s,t]的位置取反。01串初始每个位置都是0。每个区间可以随意选取多次,问01串经过操作后有多少种可能的串。答案模109+7

Solution

结论1:每个区间至多取一次。
结论2:某些区间操作后的串会与另外一些区间操作的相同,那么只用保留某几个区间(我们称它们是有用的区间)。

那么我们现在要从许多区间中找到一些有用的区间。

我们对于区间[l,r]考虑从l连一条双向边到r+1,操作完后,我们寻找连通块,那么联通块里点的个数减一就是有用的区间的个数,后面累加起来,就是总共有用区间的个数。

设它为n,那么他们能组成的个数有:2n

离散化+dfs即可。

Code

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#define fo(i,j,k) for(int i=j;i<=k;i++)#define fd(i,j,k) for(int i=j;i>=k;i--)#define N 500001#define M 1000001#define moh 400007#define mo 1000000007#define ll long longusing namespace std;int to[M],next[M],last[M],num=0;int h[moh],hh[moh];bool bz[N];int tt[N];int get(int x){    int p=x%moh;    while(h[p] && h[p]!=x) p=(p+1)%moh;    h[p]=x;    hh[p]++;    return p;}bool check(int x){    int p=x%moh;    while(h[p] && h[p]!=x) p=(p+1)%moh;    if(h[p]==x && hh[p]>1) return true;    return false;}void link(int x,int y){    num++;    to[num]=y;    next[num]=last[x];    last[x]=num;}ll pow(int n){    ll b=1;    ll m=2;    while(n)    {        if(n%2) b=b*m%mo;        n/=2;        m=m*m%mo;    }    return b;}void dfs(int x){    bz[x]=1;    for(int i=last[x];i;i=next[i])    {        int v=to[i];        if(!bz[v]) dfs(v);    }}int main(){    int T;    cin>>T;    while(T--)    {        num=0;        memset(last,0,sizeof(last));        memset(bz,0,sizeof(bz));        memset(h,0,sizeof(h));        memset(hh,0,sizeof(hh));        int n,m;        scanf("%d %d",&n,&m);        tt[0]=0;        fo(i,1,m)        {            int l,r;            scanf("%d %d",&l,&r);            r++;            int x=get(l),y=get(r);            if(!check(l)) tt[++tt[0]]=x;            if(!check(r)) tt[++tt[0]]=y;            link(x,y);            link(y,x);        }        int q=tt[0];        fo(i,1,tt[0])        if(!bz[tt[i]])        {            dfs(tt[i]);            q--;        }        printf("%lld\n",pow(q));    }}
1 0
原创粉丝点击