最近复习重新学习了一下FWT,然后做了一些乱七八糟的题目。。
我们约定:下文中的
⊕
\oplus
⊕表示异或符号,
∩
\cap
∩代表按位与,
∪
\cup
∪代表按位或,
c
n
t
[
i
]
cnt[i]
cnt[i] 表示
i
i
i 二进制上
1
1
1 的个数,带*标记的为好题集
顺便提一下 O ( n 2 2 n ) O(n^2 2^n) O(n22n)做子集卷积的方法,即求 H [ S ] = ∑ i ∪ j = S , i ∩ j = 0 F [ i ] G [ j ] H[S]=\sum_{i\cup j=S,i\cap j=0}F[i]G[j] H[S]=∑i∪j=S,i∩j=0F[i]G[j]。为了保证交起来为空集,枚举 F , G F,G F,G的元素个数 a , b a,b a,b,FWT之后只取元素个数为 a + b a+b a+b的值。
再顺便提一下,异或卷积的集合幂级数表达,也就是它正变换其实是 F [ S ] = ∑ T ( − 1 ) ∣ S ∩ T ∣ f [ T ] F[S]=\sum_{T} (-1)^{|S\cap T|}f[T] F[S]=∑T(−1)∣S∩T∣f[T]
HDU5909 Tree Cutting
对于一个选择方案,我们仅在深度最小的那个节点上计算。设
f
[
i
]
[
x
]
f[i][x]
f[i][x]表示i号节点上,异或和为x的方案数。不难得到转移方程:
f
[
u
]
[
i
]
=
∑
j
⊕
k
f
[
u
]
[
j
]
∗
f
[
v
]
[
k
]
f[u][i]=\sum_{j\oplus k}f[u][j]*f[v][k]
f[u][i]=∑j⊕kf[u][j]∗f[v][k]
用fwt优化之。
时间复杂度
O
(
n
v
log
v
)
O(nv\log v)
O(nvlogv)
BZOJ4589 Hard Nim
不难想到用
f
[
x
]
f[x]
f[x]表示局面异或和为x的方案数,每堆石子都是等价的,所以就是n次方,快速幂即可
时间复杂度
O
(
m
log
m
log
n
)
O(m\log m\log n)
O(mlogmlogn)
Codeforces 662C*
一道还挺有意思的题。注意到n很小,所以考虑把每一列上的数状压成一个整数。如果我们枚举了一个行的异或方案,那么每一列上就有唯一确定的权值,即要么全选0,要么全选1。考虑一个行的异或方案,就相当于是把每一列xor上一个数。那么我们可以构造 a i a_i ai表示有多少条列的状态为 i i i, b i b_i bi表示状态i的最小代价。
a
n
s
(
s
)
=
∑
i
⊕
s
=
j
a
i
b
j
ans(s)=\sum_{i\oplus s=j} a_ib_j
ans(s)=i⊕s=j∑aibj
由于异或满足交换律,那么有
a
n
s
(
s
)
=
∑
i
⊕
j
=
s
a
i
b
j
ans(s)=\sum_{i\oplus j=s} a_ib_j
ans(s)=i⊕j=s∑aibj
用fwt优化之,时间复杂度
O
(
n
2
n
)
O(n2^n)
O(n2n)
HAOI2015 按位或
首先要说明的一点是我们一般用
S
‾
\overline S
S来表示S的补集。
可以考虑min-max容斥,然后我们要求的就是
A
[
S
]
=
∑
S
∩
T
P
[
T
]
A[S]=\sum_{S\cap T} P[T]
A[S]=∑S∩TP[T]
但是这个不好限制转化一下,考虑到
∑
P
[
S
]
=
1
\sum P[S]=1
∑P[S]=1,那么就有
A
[
S
]
=
1
−
∑
T
⊆
S
‾
P
[
T
]
A[S]=1-\sum_{T\subseteq \overline S} P[T]
A[S]=1−∑T⊆SP[T]。后面的做一遍高维前缀和即可
时间复杂度
O
(
n
2
n
)
O(n2^n)
O(n2n)
SPOJ kosare
鉴于spoj老挂,所以洛谷
用
F
[
S
]
F[S]
F[S]表示选取的盒子并起来是S的子集的方案数,那么就可以子集反演得到恰好为S的子集的方案数。
设
f
[
S
]
f[S]
f[S]为盒子为S的子集的方案数,
F
[
s
]
=
2
f
[
S
]
−
1
F[s]=2^{f[S]}-1
F[s]=2f[S]−1,
f
f
f用高维前缀和搞一搞即可。
由于最后只询问一个值,可以不必做子集反演,可以
O
(
n
)
O(n)
O(n)地扫一遍。
时间复杂度
O
(
m
2
m
+
n
)
O(m2^m+n)
O(m2m+n)
SPOJ TLE
用
f
[
x
]
f[x]
f[x]表示考虑了前i个且以x结尾的方案数。不难得到转移方程
f
[
i
]
=
∑
i
∩
j
=
0
f
[
j
]
=
∑
j
⊆
i
‾
f
[
j
]
f[i]=\sum_{i\cap j=0} f[j]=\sum_{j\subseteq \overline i} f[j]
f[i]=∑i∩j=0f[j]=∑j⊆if[j]
每次转移完暴力清空掉
c
[
i
]
c[i]
c[i]倍数的方案即可。
时间复杂度
O
(
n
m
2
m
+
n
m
)
O(nm2^m+nm)
O(nm2m+nm)
hihocoder1496 寻找最大值*
一道还挺有意思的题目,可以考虑枚举
i
∩
j
i\cap j
i∩j的值
k
k
k,那么我们就需要知道最优的
i
,
j
i,j
i,j,那么当然是选最大的。
因为我们难以保证
i
∩
j
=
k
i\cap j=k
i∩j=k,不妨放缩条件,维护
k
k
k的超集中的最大和次大值。这样显然不会漏掉最大值,而也不可能有一个不合法的解超过最优值,因为这个不合法的解的
k
k
k肯定偏小,那么必定存在一个相应的合法的解比它更大。
枚举超集用高维前缀和解决。
时间复杂度
O
(
v
log
v
)
O(v\log v)
O(vlogv)
UOJ310 黎明前的巧克力
题意:对于给定的数,可以划分至 A A A 集合,或 B B B 集合,或不选。求 A , B A,B A,B 集合异或和为0,A,B集合不可同时为空的方案数。
考虑生成函数,对于一块值为 a i a_i ai 巧克力来说即为 2 x a i + 1 2x^{a_i}+1 2xai+1。那么答案是生成函数的异或卷积和的第 0 0 0 位,再减 1 1 1 (同时为空集)。
但是暴力FWT会TLE。注意到生成函数仅有两项,且常数只有 2 2 2 和 1 1 1 ,从正变换的表达 F [ S ] = ∑ T ( − 1 ) ∣ S ∩ T ∣ f [ T ] F[S]=\sum_{T} (-1)^{|S\cap T|}f[T] F[S]=∑T(−1)∣S∩T∣f[T] 来看,其实进行正变换之后只可能有两种取值 3 3 3 或 − 1 -1 −1。那么最后卷积结果的常数都是形如 3 x ( − 1 ) n − x 3^x(-1)^{n-x} 3x(−1)n−x ,如果能求出 x x x 即可快速得到结果。
我们其实可以快速算出各个位上的和,即 3 x + ( − 1 ) ( n − x ) 3x+(-1)(n-x) 3x+(−1)(n−x) ,因为 F W T ( A + B ) = F W T ( A ) + F W T ( B ) FWT(A+B)=FWT(A)+FWT(B) FWT(A+B)=FWT(A)+FWT(B),由正变换表达式即可得证。那么把各个生成函数加起来,做一次正变换,解出 x x x ,算出卷积结果再逆变换即可得到答案了。
时间复杂度 O ( n + v log v ) O(n+v\log v) O(n+vlogv)
HDU6057
i ∪ j = i ∩ j + i ⊕ j i\cup j=i\cap j+i\oplus j i∪j=i∩j+i⊕j,由此我们可以选择两个位运算,换掉第三个。
为了方便后面的计算,我们限制 x = i ∪ j , y = i ⊕ j x=i\cup j,y=i\oplus j x=i∪j,y=i⊕j,那么所有可能的数对 ( i , j ) (i,j) (i,j) 一共有 2 c n t [ y ] 2^{cnt[y]} 2cnt[y] 个。这个可以按位来计数,当 y y y 二进制下某一位为 1 1 1 时, ( i , j ) (i,j) (i,j) 中有且仅有一个这一位也为 1 1 1,其他情况下则必须和 x x x 的二进制位相同。
c [ k ] = ∑ x − y = k [ y ⊆ x ] A [ y ] B [ x ] 2 c n t [ y ] c[k]=\sum_{x-y=k} [y\subseteq x] A[y]B[x]2^{cnt[y]} c[k]=x−y=k∑[y⊆x]A[y]B[x]2cnt[y]
c [ k ] = ∑ x ⊕ y = k [ c n t [ x ] − c n t [ y ] = c n t [ k ] ] B [ x ] × A [ y ] 2 c n t [ y ] c[k]=\sum_{x\oplus y =k}[cnt[x]-cnt[y]=cnt[k]]B[x]\times A[y]2^{cnt[y]} c[k]=x⊕y=k∑[cnt[x]−cnt[y]=cnt[k]]B[x]×A[y]2cnt[y]
只需要枚举 c n t [ k ] cnt[k] cnt[k],剩下就类似于子集卷积的处理方法了,分组即可做到 O ( m 2 2 m ) O(m^22^m) O(m22m)。