Solution Chef and Favourite Sequence
题目大意:给定一个长为(n)的全(0)序列,以及(m)个操作([l,r]),代表将区间([l,r])翻转。问通过这(m)个操作可以生成多少种不同的序列
并查集,线性基
分析:
最终序列实际上就是选择的操作异或起来(把每个操作当做([l,r])为(1),其它为(0)的序列)
实际上有一些操作是可以被其他操作表示出来的,被表示出来的操作都是无用的。(实际上就是求这个线性空间的基)
设剩余操作有(x)个,那么答案为(2^x)
求线性基显然不能暴力求,考虑特殊性质,(1)是连续的
我们可以做一个差分,对([l,r])的修改变成(l,r+1)两点的修改
那么可以用并查集来维护,每次合并(l,r+1),如果一个区间的端点(l,r)在同一集合说明它已经被表示出来了
如果一组操作有关显然会形成一个环,也就是说我们不需要考虑操作的顺序
#include <cstdio>
#include <cctype>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 100,mod = 1e9 + 7;
inline int read(){
int x = 0;char c = getchar();
while(!isdigit(c))c = getchar();
while(isdigit(c))x = x * 10 + c - '0',c = getchar();
return x;
}
int f[maxn],n,m,ans;
inline int mul(int a,int b){return (1ll * a * b) % mod;}
inline int qpow(int a,int b){
int res = 1,base = a;
while(b){
if(b & 1)res = mul(res,base);
base = mul(base,base);
b >>= 1;
}
return res;
}
inline int find(int x){
return x == f[x] ? x : f[x] = find(f[x]);
}
inline void merge(int x,int y){
x = find(x),y = find(y);
f[x] = y;
}
int main(){
n = read(),m = read();
for(int i = 1;i <= n + 1;i++)f[i] = i;
for(int l,r,i = 1;i <= m;i++){
l = read(),r = read() + 1;
if(find(l) == find(r))continue;
merge(l,r),ans++;
}
printf("%d
",qpow(2,ans));
return 0;
}