题目大意
一个 n 位的初始为全 0 的 01 串,给定 m 个区间(1<=l[i]<=r[i]<=n),每次选择其中任意一个,将01串上对应的子段取反。执行任意次操作,求能产生多少种不同的01串。
n<=10^7, m<=10^5
【30%】n,m<=10
暴力
【60%】n<=10^7,m<=20
01串有种经典的表示方法是:第 1 位不变,对于 i>=2,第 i 位为 0 表示该位与上一位相同,为 1 表示与上一位不同。例如 0011010111100 表示为 0010111100010。
所以对于一种操作 [ l[i], r[i] ],相当于把01串(新表示法)的第 l[i] 位和第 r[i]+1 位取反。
所以每种操作可以看作是一个只含 2 个 1 的01串。如果把位数离散化一下,就变成了 long long 以内的二进制数了。然后问题变成从这 m 个数中选任意个异或起来,求结果有多少种。
所以暴力枚举每个数选还是不选,然后hash判重。
【100%】n<=10^7,m<=10^5
考虑上述思想优化。
对于操作 [ l[i], r[i] ],连一条 l[i] 到 r[i]+1 的边。假设我们可以对边进行01染色(其实就是代表做不做这个操作),每个点的颜色就等于连着它的边的颜色和 mod 2(相当于最后这个位置是 0 还是 1)。
显然对于每个连通块是独立的。而每个连通块假设有 k 个点,那方案数就是 2^(k-1)。数学归纳法证明:当只有 1 个点是成立的;对于从连通块连向新点的边,显然方案数是乘2,对于连通块内部的连边,我染0色则是原方案,我染1色则把原方案取反还是同样效果,所以方案数还是乘2。
所以假设 a 个点分成 b 个连通块,最终答案就是 2^(a-b)。
代码
1 |
|