这里会显示出您选择的修订版和当前版本之间的差别。
两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
2020-2021:teams:legal_string:王智彪:fwt [2021/07/21 15:00] 王智彪 |
2020-2021:teams:legal_string:王智彪:fwt [2021/08/14 09:08] (当前版本) 王智彪 |
||
---|---|---|---|
行 63: | 行 63: | ||
$FWT(A+B)=FWT(A)+FWT(B)$ | $FWT(A+B)=FWT(A)+FWT(B)$ | ||
- | $IFWT(A)=({\frac {IFWT(A_{0})-IFWT(A_{1})} 2},{\frac {IFWT(A_{0})-IFWT(A_{1})} 2})$ | + | $IFWT(A)=({\frac {IFWT(A_{0})+IFWT(A_{1})} 2},{\frac {IFWT(A_{0})-IFWT(A_{1})} 2})$ |
+ | |||
+ | $A_{i}=\sum_{C_{1}}A_{j}-\sum_{C_{2}}A_{j}$,( $C_{1}$ 表示 $i\&j$ 奇偶性为 $0$ ,$C_{2}$ 表示奇偶性为 $1$ ,其中奇偶性代表二进制为 $1$ 的个数的奇偶性) | ||
+ | |||
+ | ==== 同或卷积 ==== | ||
+ | |||
+ | $FWT(A)=(FWT(A_{0})-FWT(A_{1}),FWT(A_{0})+FWT(A_{1}))$ | ||
+ | |||
+ | $FWT(A+B)=FWT(A)+FWT(B)$ | ||
+ | |||
+ | $IFWT(A)=({\frac {IFWT(A_{0})-IFWT(A_{1})} 2},{\frac {IFWT(A_{0})+IFWT(A_{1})} 2})$ | ||
+ | |||
+ | $A_{i}=\sum_{C_{1}}A_{j}-\sum_{C_{2}}A_{j}$,( $C_{1}$ 表示 $i|j$ 奇偶性为 $0$ ,$C_{2}$ 表示奇偶性为 $1$ ,其中奇偶性代表二进制为 $1$ 的个数的奇偶性) | ||
===== 算法实现 ===== | ===== 算法实现 ===== | ||
行 101: | 行 113: | ||
if(opt==-1)P[k]=1ll*P[k]*inv2%MOD,P[k+p]=1ll*P[k+p]*inv2%MOD; | if(opt==-1)P[k]=1ll*P[k]*inv2%MOD,P[k+p]=1ll*P[k+p]*inv2%MOD; | ||
} | } | ||
+ | } | ||
+ | |||
+ | void tor_FWT(ll *P,int opt) {//同或卷积 | ||
+ | for(int i=2;i<=N;i<<=1) { | ||
+ | for(int p=i>>1,j=0;j<N;j+=i) { | ||
+ | for(int k=j;k<j+p;++k) { | ||
+ | ll x=P[k],y=P[k+p]; | ||
+ | P[k]=(x-y+MOD)%MOD; | ||
+ | P[k+p]=(x+y)%MOD; | ||
+ | if(opt==-1) P[k]=1ll*P[k]*inv2%MOD,P[k+p]=1ll*P[k+p]*inv2%MOD; | ||
+ | } | ||
+ | } | ||
+ | } | ||
} | } | ||
行 239: | 行 264: | ||
printf("%lld ",b1[i^sum]); | printf("%lld ",b1[i^sum]); | ||
} | } | ||
+ | return 0; | ||
+ | } | ||
+ | |||
+ | </code> | ||
+ | |||
+ | </hidden> | ||
+ | |||
+ | 2.[[https://www.luogu.com.cn/problem/P6097]] | ||
+ | |||
+ | 是一个模板题,又叫子集卷积,在原来的或卷积的基础 $i|j=k$ 上,加上了 $i\&j=0$ 的限制条件。 | ||
+ | |||
+ | $i|j=k$ 的限制条件还是用 $FWT$ 计算卷积。 | ||
+ | |||
+ | 对于第二个限制,等价于 $popcount(i)+popcount(j)=popcount(i|j)$ ,首先显然要新开一维记录每个位置的 $popcount$ 。然后让 $f_{i,j}=a_{j}(popcount(j)=i)$ ,不然 $f_{i,j}=0$ , $g_{i,j}$ 同理,然后有 $h_{i}=\sum_{k=0}^{i}f_{k}×g_{i-k}$ ,最后所求答案为 $h_{popcount(i),i}$ ,这样复杂度是 $O(n^{2}2^{n})$ 的。 | ||
+ | |||
+ | <hidden 代码> | ||
+ | |||
+ | <code cpp> | ||
+ | |||
+ | #include<bits/stdc++.h> | ||
+ | using namespace std; | ||
+ | typedef long long ll; | ||
+ | const int MAXN=1048577; | ||
+ | const ll MOD=1e9+9,inv2=(MOD+1)>>1; | ||
+ | |||
+ | int N,lim,pc[MAXN]; | ||
+ | int a2[21][MAXN],b2[21][MAXN],ans[21][MAXN]; | ||
+ | |||
+ | void or_FWT(int *P,int opt) { | ||
+ | for(int i=2; i<=N; i<<=1) | ||
+ | for(int p=i>>1,j=0; j<N; j+=i) | ||
+ | for(int k=j; k<j+p; ++k) | ||
+ | P[k+p]+=P[k]*opt,P[k+p]=(P[k+p]+MOD)%MOD; | ||
+ | } | ||
+ | |||
+ | void solve_or_FWT() { | ||
+ | for(int i=0;i<=lim;i++) or_FWT(a2[i],1),or_FWT(b2[i],1); | ||
+ | for(int i=0;i<=lim;i++) { | ||
+ | for(int j=0;j<=i;j++) { | ||
+ | for(int k=0;k<N;k++) { | ||
+ | ans[i][k]=(ans[i][k]+1ll*a2[j][k]*b2[i-j][k]%MOD)%MOD; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | for(int i=0;i<=lim;i++) or_FWT(ans[i],-1); | ||
+ | } | ||
+ | |||
+ | void print_or_FWT() { | ||
+ | for(int i=0;i<N;i++) printf("%d ",ans[pc[i]][i]); | ||
+ | putchar(10); | ||
+ | } | ||
+ | |||
+ | int main() { | ||
+ | scanf("%d",&N); | ||
+ | lim=N; | ||
+ | N=1<<N; | ||
+ | for(int i=0;i<N;i++) pc[i]=__builtin_popcount(i); | ||
+ | for(int i=0;i<N;i++) scanf("%d",&a2[pc[i]][i]); | ||
+ | for(int i=0;i<N;i++) scanf("%d",&b2[pc[i]][i]); | ||
+ | solve_or_FWT(); | ||
+ | print_or_FWT(); | ||
+ | return 0; | ||
+ | } | ||
+ | |||
+ | </code> | ||
+ | |||
+ | </hidden> | ||
+ | |||
+ | 3.[[https://ac.nowcoder.com/acm/contest/11258/E|牛客第七场E]] | ||
+ | |||
+ | ==== 题意 ==== | ||
+ | |||
+ | 给了 $n$ 堆石子和 $m$ 个可以取走的石子的数量,记为 $x_{i}$ ,除了这 $m$ 种石子,还可以取莫比乌斯函数值为 $1$ 的数量石子。同时给出这 $n$ 堆石子的数量范围 $[l_{i},r_{i}]$ ,求所有的情况中,先手必胜的局数,局面不同当且仅当存在一堆石子在两个局面中数量不同。 | ||
+ | |||
+ | $1≤n≤10^{6},1≤l_{i},r_{i}≤10^{5},1≤m≤5,1≤x_{i}≤10^{5}$ | ||
+ | |||
+ | ==== 题解 ==== | ||
+ | |||
+ | 大综合题,显然需要会莫比乌斯反演(废话),还需要会博弈论的 $sg$ 那套理论,先手必胜当且仅当所有的 $sg$ 值异或起来不为 $0$ 。听出题人说 $sg$ 值最高不会超过 $230$ ,但是现场的时候怎么知道嘛... | ||
+ | |||
+ | 显然需要先筛出来 $1$ 到 $100000$ 的莫比乌斯函数值,才能知道哪些能加进去,然后再把 $m$ 种数字也放进去,然后就是看每个状态的后继状态,这里也是学到了可以暴力搞。 | ||
+ | |||
+ | 对于每一个新的值 $i$ ,搞一个数组,看这个值的后继状态有没有 $sg$ 为这个值的,如果有需要继续找,直到找不到,根据 $mex$ 那套理论,就是此时的 $sg$ 了。然后 $i$ 加上刚才放进去的那些数的后继状态可以到达 $i$ ,于是这些状态的后继 $sg$ 值可以有现在这个值,这里我们只关心能不能有,所以可以用 $bitset$ 优化一下,这部分的复杂度是 $O({\frac {100000^{2}} w}+256×100000)$ ,这里本来要写 $230$ 的,但是后面需要变成 $256$ 。 | ||
+ | |||
+ | 于是我们处理出了所有石子数的 $sg$ 值,接下来对于每一堆石子,我们可以求出来,他们这个范围内,分别有多少个 $sg$ 值为 $0$ 的,有多少个 $sg$ 值为 $1$ 的,依次类推。 | ||
+ | |||
+ | 然后我们需要关心有多少个 $a[1]$ ^ $a[2]$ ^ $…$ ^ $a[n]≠0$ ,这玩意显然可以看成 $FWT$ 。我们先统计出每个区间内有多少个 $sg$ 值为 $i$ 的,这显然前缀和就可以。然后我们相当于看 $n$ 个多项式相乘,每个多项式的幂次是各个 $sg$ 值,系数是有多少个状态的 $sg$ 值是这个值。如果对于每个求 $FWT$ 再乘起来,最后再 $IFWT$ 。我们需要开到 $256$ ,这就照应了前面。算了一下,单个复杂度是 $O(256×log_{2}(256))=O(2048)$ ,然后 $n$ 个就是 $2×10^{9}$ 的,这显然是自杀行为。 | ||
+ | |||
+ | 然后 $oi-wiki$ 上一段话刷新了我对 $FWT$ 的认知:若我们令 $i\&j$ 中 $1$ 的奇偶性为 $i$ 与 $j$ 的奇偶性,于是 $i$ 与 $k$ 的奇偶性异或 $j$ 与 $k$ 的奇偶性等于 $i$ ^ $j$ 与 $k$ 的奇偶性。然后可以得到异或的 $FWT$ : $A_{i}=\sum_{C_{1}}A_{j}-\sum_{C_{2}}A_{j}$ , $C_{1}$ 表示 $i\&j$ 的奇偶性为偶, $C_{2}$ 表示 $i\&j$ 的奇偶性为奇。(其实记住就行...) | ||
+ | |||
+ | 又因为 $sg$ 的范围有限,所以我们可以先预处理出每个数的二进制有多少位为 $1$ (或者直接 $\_\_builtin\_popcount$ 也可以)。 | ||
+ | |||
+ | 然后对于前缀和数组,就不能直接暴力加一了,判断两者与的奇偶性,如果为偶,则加一,不然减一。然后这样就直接把每一堆的 $FWT$ 数组给求完了,复杂度变成 $O(256n)$ ,而不是原来的 $O(2048n)$ ,再全乘起来求一个 $IFWT$ 就可以了,整个这部分的复杂度都是 $O(256n)$ 的,最后对于所有 $sg$ 值不为 $0$ 的结果都加起来就是答案了。 | ||
+ | |||
+ | 另外这题卡常!前缀和数组必须大的做第一维,我不倒过来会 $t$ 掉绝大多数数据,倒过来效率就第一了... | ||
+ | |||
+ | <hidden 代码> | ||
+ | |||
+ | <code cpp> | ||
+ | |||
+ | const int maxn=100000,N=1e6+10,maxm=256,MOD=1e9+7,inv2=500000004; | ||
+ | bool check[maxn+1]; | ||
+ | int prime[maxn+1],mu[maxn+1]; | ||
+ | int tot,n,m,ls[N],rs[N],sg[maxn+1],sum[maxn+1][maxm+1],ans[maxm+1],o[maxm+1]; | ||
+ | bitset<maxn+1> t,b[maxm]; | ||
+ | |||
+ | |||
+ | void Moblus() { | ||
+ | mu[1]=1; | ||
+ | for(int i=2; i<=maxn; i++) { | ||
+ | if(!check[i]) { | ||
+ | mu[i]=-1; | ||
+ | prime[tot++]=i; | ||
+ | } | ||
+ | for(int j=0; j<tot; j++) { | ||
+ | if(i*prime[j]>maxn) break; | ||
+ | check[i*prime[j]]=true; | ||
+ | if(i%prime[j]==0) { | ||
+ | mu[i*prime[j]]=0; | ||
+ | break; | ||
+ | } else { | ||
+ | mu[i*prime[j]]=-mu[i]; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void xor_FWT(int *P,int opt,int N) { | ||
+ | for(int i=2; i<=N; i<<=1) | ||
+ | for(int p=i>>1,j=0; j<N; j+=i) | ||
+ | for(int k=j; k<j+p; ++k) { | ||
+ | int x=P[k],y=P[k+p]; | ||
+ | P[k]=((ll)x+y)%MOD; | ||
+ | P[k+p]=((ll)x-y+MOD)%MOD; | ||
+ | if(opt==-1)P[k]=(ll)P[k]*inv2%MOD,P[k+p]=(ll)P[k+p]*inv2%MOD; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void init() { | ||
+ | o[0]=0; | ||
+ | for(int i=1;i<maxm;i++)o[i]=o[i>>1]+(i&1); | ||
+ | for(int i=1; i<=maxn; i++) { | ||
+ | if(mu[i]==1)t.set(i); | ||
+ | } | ||
+ | for(int i=0; i<=maxn; i++) { | ||
+ | sg[i]=0; | ||
+ | while(b[sg[i]][i]) sg[i]++; | ||
+ | b[sg[i]]|=(t<<i); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | int main() { | ||
+ | Moblus(); | ||
+ | read(n);read(m); | ||
+ | for(int i=1; i<=n; i++) read(ls[i]),read(rs[i]); | ||
+ | for(int i=1,tmp; i<=m; i++) read(tmp),t.set(tmp); | ||
+ | init(); | ||
+ | for(int i=0; i<=maxn; i++) | ||
+ | for(int j=0; j<maxm; j++) | ||
+ | if(!(o[sg[i]&j]&1))sum[i][j]=1; | ||
+ | else sum[i][j]=MOD-1; | ||
+ | for(int i=1; i<=maxn; i++) { | ||
+ | for(int j=0; j<maxm; j++) { | ||
+ | sum[i][j]=sum[i][j]+sum[i-1][j]; | ||
+ | if(sum[i][j]>=MOD) sum[i][j]-=MOD; | ||
+ | } | ||
+ | } | ||
+ | for(int i=0; i<maxm; i++)ans[i]=1; | ||
+ | for(int i=1; i<=n; i++) | ||
+ | for(int j=0; j<maxm; j++) | ||
+ | ans[j]=(ll)ans[j]*(sum[rs[i]][j]-sum[ls[i]-1][j])%MOD; | ||
+ | xor_FWT(ans,-1,maxm); | ||
+ | ll sum=0; | ||
+ | for(int i=1; i<maxm; i++) { | ||
+ | sum=sum+ans[i]; | ||
+ | if(sum>=MOD) sum-=MOD; | ||
+ | } | ||
+ | printf("%lld\n",(sum+MOD)%MOD); | ||
return 0; | return 0; | ||
} | } |