题目链接:http://v.qzyz.com/contest/291/problem/1
题目大意:有一个长度为n的01串,初始时每个位置都是0,给定m个区间[li,ri],每次从中选择一个区间,将区间里的位置全部取反,操作可以进行任意次,求这个01串有多少种可能,输出答案模(10^9+7)。输入共T组数据。
数据范围:n<=10 000 000,m<=100 000,1<=li<=ri<=n,T<=10
题解:考虑原序列a的差分序列b,这里b[i]=a[i]^a[i-1],即a[i]=b[1]^b[2]^…^b[i],对一个区间[li,ri]取反时,可以通过对b[li]和b[ri+1]取反来实现,于是我们可以知道b中只有端点位置(li和ri+1)会等于1,其余位置都是0。
我们可以把问题转换成图论模型,对于一个区间[li,ri],在li和ri+1之间连一条无向边。于是问题就变成了:在一张图中给每条边赋值0/1,每个点的权值为与它相连的边的权值的异或和。这样我们可以独立计算每个连通块的答案。
设f(i)表示大小为 i 的连通块的答案,那么f(i)=2^(i-1)。
证明:显然f(1)=1=2^(1-1)。假设当前有一个大小为 i 的连通块,当新增加一条块内的边(u,v)时,若(u,v)=0则不影响结果,若(u,v)=1则必定可以找到一条u到v的路径将上面的边全部取反,同样不影响结果。当新增一条块内的点u与块外的点v的连边(u,v)时,若(u,v)=0则b[v]=0,其余的与原有方案一样;若(u,v)=1则必定在原有方案的基础上改变点u和点v的状态。所以此时方案数*2,即f(i+1)=f(i)*2。证毕。
所以我们只需要做一次图遍历,答案等于2^((每个连通块的点数-1)之和)
代码如下:
#include <algorithm>
#include <cstdio>
using namespace std;
const int mo=1000000007;
int a[200005],to[200005],ne[200005],fi[10000005],u[10000005],
d[200005],i,n,m,T,x,y,t,tot,cnt,ans;
void add(int x,int y)
{
to[++tot]=y;ne[tot]=fi[x];fi[x]=tot;
}
void bfs(int x)
{
int i,s=1,t=1;
for (u[d[1]=x]=1;s<=t;++s)
for (i=fi[d[s]];i;i=ne[i])
if (!u[to[i]]) u[d[++t]=to[i]]=1;
cnt+=t-1;
}
int main()
{
for (scanf("%d\n",&T);T--;tot=0)
{
scanf("%d%d\n",&n,&m);t=cnt=0;
for (i=1;i<=m;++i)
{
scanf("%d%d\n",&x,&y);++y;
a[++t]=x;a[++t]=y;
add(x,y);add(y,x);
}
for (i=1;i<=t;++i)
if (!u[a[i]]) bfs(a[i]);
ans=1;
for (i=1;i<=cnt;++i) ans=(ans<<1)%mo;
printf("%d\n",ans);
for (i=1;i<=t;++i) fi[a[i]]=u[a[i]]=0;
}
return 0;
}