题意
给出长度为
n
n
的数组,对于所有满足下列限制的五元组
(a,b,c,d,e)
(
a
,
b
,
c
,
d
,
e
)
1.
1 ≤a, b, c, d, e ≤ n
1
≤
a
,
b
,
c
,
d
,
e
≤
n
2.
(sa|sb)
(
s
a
|
s
b
)
&
sc
s
c
&
(sd
(
s
d
^
se) = 2i
s
e
)
=
2
i
for some integer i
3.
sa
s
a
&
sb = 0
s
b
=
0
求
∑fib(sa|sb) ∗ fib(sc) ∗ fib(sd
∑
f
i
b
(
s
a
|
s
b
)
∗
f
i
b
(
s
c
)
∗
f
i
b
(
s
d
^
se)
s
e
)
。
fibi
f
i
b
i
是斐波那契数列的第
i
i
项。
分析
这题要用到子集卷积和FWT,都完美触及到我的知识盲区,在网上查了点资料,打算写篇博客记录一下。
首先注意到
max(n)>max(si)
max
(
n
)
>
max
(
s
i
)
,所以可以考虑用
ax
a
x
表示有
ax
a
x
个
si
s
i
等于
x
x
。
那么原问题大概可以通过以下步骤解决:
1.求数组使得
Ai=fibi∗∑j∑k[j|k==i
A
i
=
f
i
b
i
∗
∑
j
∑
k
[
j
|
k
==
i
&&
j∩k==∅]∗aj∗ak
j
∩
k
==
∅
]
∗
a
j
∗
a
k
2.求
B
B
数组使得
3.求
C
C
数组使得^
k==i]∗aj∗ak
k
==
i
]
∗
a
j
∗
a
k
4.求
D
D
数组使得&
k==i]∗Aj∗Bk
k
==
i
]
∗
A
j
∗
B
k
5.求
E
E
数组使得&
k==i]∗Dj∗Ck
k
==
i
]
∗
D
j
∗
C
k
1是子集卷积,345都可以 FWT F W T 搞搞。
子集卷积直接做有一种
O(3n)
O
(
3
n
)
的做法,于是去学习了一下
O(n2∗2n)
O
(
n
2
∗
2
n
)
的姿势。不过网上貌似没有很多我看的懂的资料。查了很久查到了讲集合并卷积的东西。。仔细想了一下可能做法差不多。。?
集合并卷积是求
fi=∑j∑k[j|k==i]∗aj∗ak
f
i
=
∑
j
∑
k
[
j
|
k
==
i
]
∗
a
j
∗
a
k
,也就是两个子集交集可以不为空。
考虑这样一个二维上的问题:求
fi=∑max(r1,c1)==i∑max(r2,c2)==iar1,c1∗ar2,c2
f
i
=
∑
max
(
r
1
,
c
1
)
==
i
∑
max
(
r
2
,
c
2
)
==
i
a
r
1
,
c
1
∗
a
r
2
,
c
2
有一种做法是求一次二维前缀和后,将前缀和自己做一次点积,然后沿着对角线做差分就是答案。
对应到求集合并卷积可能也是差不多的道理(强行意念差不多)。做一次高维前缀和之后点积一下然后高维差分就是答案。
子集卷积实际上就是把集合大小相同的集合都放在了一起,每种集合大小都各自跑集合并卷积,点积的时候把不同集合大小的卷起来就行。。
FWT
F
W
T
的证明
YY
Y
Y
了好久。。最后
xjb
x
j
b
想了一下假装自己会了。。
for(int d=1;d<n;d<<=1)for(i=0;i<n;i+=d*2)for(j=0;j<d;++j){
//x=a[i+j],y=a[i+j+d]
//正变换
//and a[i+j]=x+y
//or a[i+j+d]=x+y
//xor a[i+j]=x+y,a[i+j+d]=x-y
//逆变换
//and a[i+j]=x-y
//or a[i+j+d]=y-x
//xor a[i+j]=(x+y)/2,a[i+j+d]=(x-y)/2
}
对于
and
a
n
d
,考虑新增的位,
f0
f
0
和
f1
f
1
都被加到
f′0
f
0
′
上,也就是
f′0=f0+f1,f′1=f1
f
0
′
=
f
0
+
f
1
,
f
1
′
=
f
1
,点积之后就是
h0=f′0∗g′0=(f0+f1)∗(g0+g1)=f0∗g0+f0∗g1+f1∗g0+f1∗g1
h
0
=
f
0
′
∗
g
0
′
=
(
f
0
+
f
1
)
∗
(
g
0
+
g
1
)
=
f
0
∗
g
0
+
f
0
∗
g
1
+
f
1
∗
g
0
+
f
1
∗
g
1
h1=f′1∗g′1=f1∗g1
h
1
=
f
1
′
∗
g
1
′
=
f
1
∗
g
1
显然
h0
h
0
多了一项
f1∗g1
f
1
∗
g
1
,因为
1
1
&,而这一项恰好等于
h1
h
1
,在逆变换中减去即可。将每一位多出的答案依次减掉之后最后就是所求。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1<<17;
const int MOD=1000*1000*1000+7;
int n,inv2,a[N],fib[N],bits[N],b[18][N],c[18][N],A[N],B[N],C[N];
inline void read(int&x){char c;while((c=getchar())<'0'||c>'9');x=c-'0';while((c=getchar())>='0'&&c<='9')x=x*10+c-'0';}
inline void add(int&x,int y){x=x+y<MOD?x+y:x+y-MOD;}
inline int he(int x,int y){return x+y<MOD?x+y:x+y-MOD;}
int Pow(int x,int y){int t=1;for(;y;y>>=1,x=1LL*x*x%MOD)if(y&1)t=1LL*t*x%MOD;return t;}
int main(){
int i,j,d,S;
read(n);
inv2=Pow(2,MOD-2);
for(i=1;i<=n;++i)read(j),++a[j];
for(fib[1]=1,i=2;i<N;++i)fib[i]=he(fib[i-1],fib[i-2]);
for(i=1;i<N;++i)bits[i]=bits[i>>1]+(i&1);
for(i=0;i<N;++i)b[bits[i]][i]=a[i];
for(i=0;i<18;++i){
for(j=0;j<17;++j)
for(S=0;S<N;++S)if(1<<j&S){
add(b[i][S],b[i][S^(1<<j)]);
}
}
for(i=0;i<18;++i){
for(j=0;j<=i;++j){
for(S=0;S<N;++S){
add(c[i][S],1LL*b[j][S]*b[i-j][S]%MOD);
}
}
}
for(i=0;i<18;++i){
for(j=0;j<17;++j){
for(S=1;S<N;++S)if(1<<j&S){
add(c[i][S],MOD-c[i][S^(1<<j)]);
}
}
}
for(i=0;i<N;++i)A[i]=1LL*c[bits[i]][i]*fib[i]%MOD;
for(i=0;i<N;++i)B[i]=1LL*a[i]*fib[i]%MOD,C[i]=a[i];
for(d=1;d<N;d<<=1){
for(i=0;i<N;i+=(d<<1)){
for(j=0;j<d;++j){
add(A[i+j],A[i+j+d]);
add(B[i+j],B[i+j+d]);
int x=C[i+j],y=C[i+j+d];
C[i+j]=he(x,y);
C[i+j+d]=he(x,MOD-y);
}
}
}
for(i=0;i<N;++i)A[i]=1LL*A[i]*B[i]%MOD,C[i]=1LL*C[i]*C[i]%MOD;
for(d=1;d<N;d<<=1){
for(i=0;i<N;i+=(d<<1)){
for(j=0;j<d;++j){
add(A[i+j],MOD-A[i+j+d]);
int x=C[i+j],y=C[i+j+d];
C[i+j]=1LL*(x+y)*inv2%MOD;
C[i+j+d]=1LL*(x-y+MOD)*inv2%MOD;
}
}
}
for(i=0;i<N;++i)C[i]=1LL*C[i]*fib[i]%MOD;
for(d=1;d<N;d<<=1){
for(i=0;i<N;i+=(d<<1)){
for(j=0;j<d;++j){
add(A[i+j],A[i+j+d]);
add(C[i+j],C[i+j+d]);
}
}
}
for(i=0;i<N;++i)A[i]=1LL*A[i]*C[i]%MOD;
for(d=1;d<N;d<<=1){
for(i=0;i<N;i+=(d<<1)){
for(j=0;j<d;++j){
add(A[i+j],MOD-A[i+j+d]);
}
}
}
int ans=0;
for(i=0;i<17;++i)add(ans,A[1<<i]);
printf("%d\n",ans);
return 0;
}