[COCI2021-2022#1] Set - 题解
SAMSHAWCRAFT · · 题解
这里给一个三进制快速沃尔什变换的做法,可能做得比较麻烦。
本题题意比较抽象,我们需要从题目信息中寻找一些三元组的性质。这里有一个性质可供使用:对于同一位上的三个数字
考虑到本质不同三元组个数为
我们在本题中只需要对
关于减
关于 k 进制 FWT,这个知识和 FFT 以及二进制 FWT 很相似,我在这里放一个 k 进制 FWT 的转移矩阵,大家可以参考这个矩阵实现任意进制 FWT。
FWT and
转移矩阵:
FWT and 相当于做后缀和,逆运算是差分。
FWT or
转移矩阵:
FWT or 相当于做前缀和,逆运算是差分。
FWT xor
转移矩阵:
其中
逆转移矩阵:
本题做的三进制 FWT 中,
我们在 IFWT 的过程中,每一层是需要对点值多项式每一项除以 3 的。当然,由于 IFWT 一共
每次都除以 3 的写法:
void FWT3XOR(int limit,complex *arr,int sign){
for(int l=1;l<limit;l*=3){
for(int cx=0;cx<limit;cx+=3*l){
for(int cy=0;cy<l;++cy){
complex tmp0=arr[cx+cy+l*0];
complex tmp1=arr[cx+cy+l*1];
complex tmp2=arr[cx+cy+l*2];
if(sign==1){
arr[cx+cy+l*0]=tmp0+tmp1+tmp2;
arr[cx+cy+l*1]=tmp0+tmp1*w+tmp2*w2;
arr[cx+cy+l*2]=tmp0+tmp1*w2+tmp2*w;
}else{
arr[cx+cy+l*0]=(tmp0+tmp1+tmp2)/3;
arr[cx+cy+l*1]=(tmp0+tmp1*w2+tmp2*w)/3;
arr[cx+cy+l*2]=(tmp0+tmp1*w+tmp2*w2)/3;
}
}
}
}
}
最后除以
void FWT3XOR(int limit,complex *arr,int sign){
for(int l=1;l<limit;l*=3){
for(int cx=0;cx<limit;cx+=3*l){
for(int cy=0;cy<l;++cy){
complex tmp0=arr[cx+cy+l*0];
complex tmp1=arr[cx+cy+l*1];
complex tmp2=arr[cx+cy+l*2];
if(sign==1){
arr[cx+cy+l*0]=tmp0+tmp1+tmp2;
arr[cx+cy+l*1]=tmp0+tmp1*w+tmp2*w2;
arr[cx+cy+l*2]=tmp0+tmp1*w2+tmp2*w;
}else{
arr[cx+cy+l*0]=(tmp0+tmp1+tmp2);
arr[cx+cy+l*1]=(tmp0+tmp1*w2+tmp2*w);
arr[cx+cy+l*2]=(tmp0+tmp1*w+tmp2*w2);
}
}
}
}
if(sign==-1){
for(int cx=0;cx<limit;++cx)
arr[cx]=arr[cx]/limit;
}
}
两种写法等价,但后者应该更快(做除法次数少)。
本题完整代码如下,仅供参考。
#include <stdio.h>
#include <ctype.h>
#include <cmath>
#include <algorithm>
#include <numeric>
#define qaq inline
const int sz=1e6+19;
using ll=long long;
int n,k,tup[sz];
ll ans=0;
struct complex{
long double real,imag;
complex()=default;
complex(long double a,long double b){
real=a,imag=b;
}
qaq friend complex operator+(complex a,complex b){
return complex(a.real+b.real,a.imag+b.imag);
}
qaq friend complex operator-(complex a,complex b){
return complex(a.real-b.real,a.imag-b.imag);
}
qaq friend complex operator*(complex a,complex b){
complex res;
res.real=a.real*b.real-a.imag*b.imag;
res.imag=a.real*b.imag+a.imag*b.real;
return res;
}
qaq friend complex operator*(complex a,long double b){
return complex(a.real*b,a.imag*b);
}
qaq friend complex operator/(complex a,long double b){
return complex(a.real/b,a.imag/b);
}
}pl[sz];
const complex w=complex(-0.5,0.5*std::sqrt(3));
const complex w2=complex(-0.5,-0.5*std::sqrt(3));
void FWT3XOR(int limit,complex *arr,int sign){
for(int l=1;l<limit;l*=3){
for(int cx=0;cx<limit;cx+=3*l){
for(int cy=0;cy<l;++cy){
complex tmp0=arr[cx+cy+l*0];
complex tmp1=arr[cx+cy+l*1];
complex tmp2=arr[cx+cy+l*2];
if(sign==1){
arr[cx+cy+l*0]=tmp0+tmp1+tmp2;
arr[cx+cy+l*1]=tmp0+tmp1*w+tmp2*w2;
arr[cx+cy+l*2]=tmp0+tmp1*w2+tmp2*w;
}else{
arr[cx+cy+l*0]=(tmp0+tmp1+tmp2)/3;
arr[cx+cy+l*1]=(tmp0+tmp1*w2+tmp2*w)/3;
arr[cx+cy+l*2]=(tmp0+tmp1*w+tmp2*w2)/3;
}
}
}
}
}
int main(){
scanf("%d%d",&n,&k);
for(int cx=0,u;cx<n;++cx){
u=0;
char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) u=u*3+ch-'1',ch=getchar();
tup[u]++,pl[u]=complex(1,0);
}
int lim,limbit3;
for(lim=1,limbit3=0;limbit3<k;lim*=3,++limbit3);
FWT3XOR(lim,pl,1);
for(int cx=0;cx<lim;++cx)
pl[cx]=pl[cx]*pl[cx]*pl[cx];
FWT3XOR(lim,pl,-1);
ans=pl[0].real+0.5;
printf("%lld\n",(ans-n)/6);
return 0;
}