之前没认真学 F W T FWT FWT可惜了
首先要做过这道题 [AGC034F] RNG and XOR 。
考虑 I F W T IFWT IFWT算法的本质
之前我们注意到将 k k k的顺序调换并不会影响结果,也就是说只要做一遍 F W T FWT FWT,然后再除以 2 n 2^n 2n就是答案。
考虑优化,发现从 P ^ i \widehat P_i P i变成 P ^ i + 2 j \widehat P_{i+2^j} P i+2j相当于整体偏移一个定值,可以用背包记录!最后再记录一下奇偶性就好了!
复杂度 O ( n ∑ p ) O(n\sum p) O(n∑p)。
remark \text{remark} remark 感觉这部分算法的性质蛮多的!
#include
#define ll long long
#define fi first
#define se second
#define pb push_back
#define db double
using namespace std;
const int mod=998244353;
const int N=1e5+5;
int n,a[105],p[105],s;
ll now[N][2],nxt[N][2],res;
void add(ll &x,ll y){
x=(x+y)%mod;
}
ll fpow(ll x,ll y=mod-2){
ll z(1);
for(;y;y>>=1){
if(y&1)z=z*x%mod;
x=x*x%mod;
}return z;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n;for(int i=0;i<n;i++)cin>>a[i];
for(int i=0;i<n;i++)cin>>p[i],s+=p[i];
now[2*s][0]=1;
for(int i=0;i<n;i++){
memset(nxt,0,sizeof nxt);
for(int j=0;j<=2*s;j++){
for(int k=0;k<2;k++){
if(now[j][k]){
add(nxt[j][k],now[j][k]);
add(nxt[j-2*p[i]][k^a[i]],now[j][k]);
}
}
}
memcpy(now,nxt,sizeof nxt);
}ll mul=fpow(s);
for(int i=0;i<=2*s;i++){
if(now[i][1]){
add(res,2*fpow(1-(i-s)*mul%mod)*now[i][1]%mod);
}
}
cout<<(res+mod)%mod;
}