E题 fft+异或
之前校赛网络赛的最后一题卡了几天也没做出来,但是一直给我一种能做的感觉。最后怎么写都tle,窝当时说一定是用了什么神奇的算法,赛后得知是fft。由于当时ddl很多就没补,拖到现在,熊猫杯又遇到了这种“我感觉我能写但是怎么写都tle他一定是用了什么神奇的东西”的熟悉的感觉,当时就隐隐觉得会不会是fft,还问了队长一句,最后果然是。女人的直觉太可怕了 所以拖到现在还是来补啦。
题目链接 链接:link.
题意:大概意思是给你一个n的数组,进行k轮操作,每轮操作对于a[2]到a[n]来说,后一个数跟前一个数异或并更新该位置的值。
思路
假设原来是
a b c d e
第一次操作之后
a ab abc abcd abcde
第二次操作之后
a aab aaabbc aaaabbbccd aaaaabbbbcccdde
第三次操作之后
a aaab aaaaaabbbc 。。。
发现规律了吗铁汁萌
由打表可知
好像存在一个比较神奇的序列b,序列b大概是这样的:
C(k-1,k-1) C(k,k-1) C(k+1,k-1) C(k+2,k-1)…
具体为啥是这样我也不知道反正打表打出来就是这样qaq 貌似是什么k次前缀和的生成函数进行广义二项式展开 我也不懂qaq反正知道这个结论了就行 多写几项就看出来了
a1对ans[i]的贡献为a1b[i]…
a2对ans[i]的贡献为a2b[i+1]…
a3对ans[i]的贡献为a3b[i+2]…
…
an对ans[i]的贡献为an*b[i+n-1]…
然后ans是把这些贡献全都异或起来
好像应该用数学语言描述一下但是我不太会用这个东西所以就将就看吧 这样看应该比数学公式更直观
等等 这个东西好像有点眼熟 阿
像不像多项式的乘法但是最后加的部分变成了异或?
多项式乘法的头和异或的尾巴?
1e5的范围当然是考虑fft啦
当时我竟然还行1e10莽过去幸好队长劝住了我
万能的群友告诉我异或也许是fwt然后我又去学了一下fwt发现 这玩意套不上阿 然后又想改fft的板子 但是又学了一遍原理之后感觉也改不了qaq 最后做法竟然是做十七遍fft分别异或我人傻了 果然暴力才是yyds
好了 确定是17遍fft之后,那异或怎么处理呢。
首先考虑,对于每一位分别在模2下做fft得到ans数组每一位的二进制位的大小,最后再乘以2的对应次方加起来就行。那就是多项式乘法呀,最后对2取模就好。再来看看这个组合数怎么处理(因为他太大了一定会爆longlong的)
异或偶数次相当于没异或,所以我们只需要知道这个组合数的奇偶就好。
有个很神奇的O(1)的判断可以做到,就是如果C(m,n)是奇数,当且仅当m&n=n。
具体证明在这:
链接:link.
然后,讲fft的博客也很多,我现学的也不熟,这里就不做赘述了。板子链接:链接:link.
最后 一定要记得初始化 不然就会像我一样输出一堆乱七八糟的数手足无措。
AC代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const ll N = 4e6+10;
const double pi = acos(-1.0);
using namespace std;
struct complex{
double x,y;
complex(double xx=0,double yy=0){x=xx,y=yy;}
complex operator +(const complex &a){return complex(x+a.x,y+a.y);}
complex operator -(const complex &a){return complex(x-a.x,y-a.y);}
complex operator *(const complex &a){return complex(x*a.x-y*a.y,x*a.y+y*a.x);}
};
ll r[N];
complex a[N],b[N];
void fft(complex* a,ll n,ll type){
for(ll i=0;i<n;i++){
if(i<r[i])
swap(a[i],a[r[i]]);
}
for(ll mid=1;mid<n;mid<<=1){
complex Wn=complex(cos(pi/mid),type*sin(pi/mid));
for(ll R=mid<<1,j=0;j<n;j+=R){
complex w(1,0);
for(ll k=0;k<mid;k++,w=w*Wn){
complex x=a[j+k],y=w*a[j+mid+k];
a[j+k]=x+y;
a[j+mid+k]=x-y;
}
}
}
}
ll aa[100100];
ll jiou[100100];
ll ans[100100];
ll poww(ll a, ll b)
{
ll ans = 1, base = a;
while (b != 0)
{
if (b & 1 != 0)
ans *= base;
base *= base;
b >>= 1;
}
return ans;
}
int main( )
{
long long nn,kk;
scanf("%lld %lld",&nn,&kk);
for(long long i=0;i<nn;i++)
scanf("%lld",&aa[i]);
for(ll i=kk-1;i<=kk-1+nn;i++)
{
if(((kk-1)&(i))==(kk-1))
jiou[i-kk+1]=1;
else jiou[i-kk+1]=0;
}
for(ll haha=1;haha<=17;haha++)
{
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(r,0,sizeof(r));
ll n,m,x;
n=nn-1;m=nn-1;
for(ll i=0;i<=n;i++) //x=aa[i],a[i].x = x,a[i].y=0;
{
x=aa[i]%2;
aa[i]=aa[i]/2;
a[i].x = x;
a[i].y=0;
}
for(ll i=0;i<=m;i++) x=jiou[i],b[i].x = x,b[i].y=0;
ll len = 1,l=0;
while(len <= (n+m)) len *= 2,l++;
for(ll i=0;i<len;i++)
r[i] =(r[i>>1]>>1)|((i&1)<<(l-1));
fft(a,len,1);
fft(b,len,1);
for(ll i=0;i<=len;i++)
{
a[i] = a[i] * b[i];
}
fft(a,len,-1);
for(ll i=0;i<=n;i++)
{
ll anss=ll(a[i].x/(len)+0.5);
anss=anss%2;
anss=(anss+2)%2;
//cout<<"anss="<<anss<<endl;
ans[i]=ans[i]+(poww(2,haha-1))*anss;
}
/*
cout<<"haha="<<haha<<endl;
for(ll i=0;i<nn;i++)
cout<<ans[i]<<" ";
cout<<endl;*/
}
for(ll i=0;i<nn;i++)
{
if(i!=0) printf(" ");
printf("%d",ans[i]);
}
printf("\n");
return 0;
}
等我有时间就把校赛网络赛的最后一题也补了,听说也是fft 傅里叶真聪明阿呜呜呜
G题 待补
J题 待补