1. 程式人生 > 實用技巧 >題解 CHSEQ22 【Chef and Favourite Sequence】

題解 CHSEQ22 【Chef and Favourite Sequence】

題目連結

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\n",qpow(2,ans));
	return 0;
}