前言:
因为懒,所以 写成题表模式。
bzoj 4836
假如只有一个操作,那么直接做卷积就好了,减法可以将下面的哪个数组翻转过来。
现在要求只能与大的数卷,那么就分治+fft就好了。
code:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#define LL long long
using namespace std;
const double pi=acos(-1);
int m1,m2;
int a[50010],b[50010];
LL c[100010];
int bin[200010];
struct P{
double x,y;
P() {x=y=0;}
P(double a,double b) {x=a;y=b;}
}A[200010],B[200010];
P operator+(P x,P y){return P(x.x+y.x,x.y+y.y);}
P operator-(P x,P y){return P(x.x-y.x,x.y-y.y);}
P operator*(P x,P y){return P(x.x*y.x-x.y*y.y,x.y*y.x+x.x*y.y);}
inline int read(int &x)
{
int ans=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {ans=(ans<<1)+(ans<<3)+(ch^48);ch=getchar();}
x=x>ans?x:ans;return ans*f;
}
void fft(P *a,int n,int op)
{
for(int i=0;i<n;i++) bin[i]=(bin[i>>1]>>1)|((i&1)*(n>>1));
for(int i=0;i<n;i++) if(i<bin[i]) swap(a[i],a[bin[i]]);
for(int i=1;i<n;i<<=1)
{
P wn=P(cos(pi/i),op*sin(pi/i)),t,w;
for(int j=0;j<n;j+=i<<1)
{
w=P(1,0);
for(int k=0;k<i;k++)
{
t=a[i+j+k]*w;w=w*wn;
a[i+j+k]=a[j+k]-t;a[j+k]=a[j+k]+t;
}
}
}
}
void mul(P *A,P *B,int n)
{
fft(A,n,1);fft(B,n,1);
for(int i=0;i<n;i++) A[i]=A[i]*B[i];fft(A,n,-1);
}
void solve(int l,int r)
{
if(l==r) return;
int mid=(l+r)/2;
int n=1;while(n<=(r-l+1)) n<<=1;
for(int i=0;i<n;i++) A[i]=P(0,0),B[i]=P(0,0);
for(int i=0;i<mid-l+1;i++) A[i].x=a[l+i];
for(int i=0;i<r-mid;i++) B[i].x=b[mid+i+1];
mul(A,B,n);for(int i=0;i<n;i++) c[i+l+mid+1]+=(LL)(A[i].x/n+0.5);
for(int i=0;i<n;i++) A[i]=P(0,0),B[i]=P(0,0);
for(int i=0;i<r-mid;i++) A[i].x=a[mid+i+1];
for(int i=0;i<mid-l+1;i++) B[i].x=b[mid-i];
mul(A,B,n);for(int i=0;i<n;i++) c[i+1]+=(LL)(A[i].x/n+0.5);
solve(l,mid);solve(mid+1,r);
}
int main()
{
int T;scanf("%d",&T);
while(T--)
{
int n,m,q;n=read(m1);m=read(m1);q=read(m1);
memset(a,0,sizeof(a));m1=m2=0;
memset(b,0,sizeof(b));
memset(c,0,sizeof(c));
for(int i=1;i<=n;i++) a[read(m1)]++;
for(int j=1;j<=m;j++) b[read(m2)]++;
solve(0,max(m1,m2));
for(int i=0;i<=m1;i++) c[0]+=(LL)a[i]*(LL)b[i];
while(q--) printf("%lld\n",c[read(m1)]);
}
}
bzoj 3509
直接做不好做,考虑分块。
块内的直接
n2
n
2
dp,对于三个数都在不同块的情况就枚举中间数在哪个块,然后fft。
块的大小用1800比较优。
code:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<cstdlib>
#define LL long long
using namespace std;
int n,a[100010];
const double pi=acos(-1);
struct P{
double x,y;
P() {x=y=0;}
P(double a,double b) {x=a;y=b;}
}A[70010],B[70010];
int inf=30000;
int g1[70010],g2[70010];
int pre[100010],len=1800,L[70],R[70];
int bin[70010];
LL ans=0;
P operator+(P x,P y){return P(x.x+y.x,x.y+y.y);}
P operator-(P x,P y){return P(x.x-y.x,x.y-y.y);}
P operator*(P x,P y){return P(x.x*y.x-x.y*y.y,x.y*y.x+x.x*y.y);}
inline int read()
{
int ans=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {ans=(ans<<1)+(ans<<3)+(ch^48);ch=getchar();}
return ans*f;
}
void fft(P *a,int n,int op)
{
for(int i=0;i<n;i++) bin[i]=(bin[i>>1]>>1)|((i&1)*(n>>1));
for(int i=0;i<n;i++) if(i<bin[i]) swap(a[i],a[bin[i]]);
for(int i=1;i<n;i<<=1)
{
P wn=P(cos(pi/i),op*sin(pi/i)),t,w;
for(int j=0;j<n;j+=i<<1)
{
w=P(1,0);
for(int k=0;k<i;k++)
{
t=a[i+j+k]*w;w=w*wn;
a[i+j+k]=a[j+k]-t;a[j+k]=a[j+k]+t;
}
}
}
if(op==-1) for(int i=0;i<n;i++) a[i].x/=n;
}
void solve(int l,int r)
{
for(int i=l;i<=r;i++) g1[a[i]]--;
for(int i=0;i<(1<<16);i++) A[i]=P(g1[i],0),B[i]=P(g2[i],0);
fft(A,1<<16,1);fft(B,1<<16,1);
for(int i=0;i<(1<<16);i++) A[i]=A[i]*B[i];
fft(A,1<<16,-1);
for(int i=l;i<=r;i++) ans+=(LL)(A[a[i]<<1].x+0.5);
for(int i=l;i<=r;i++)
{
for(int j=i+1;j<=r;j++)
{
if(2*a[j]-a[i]>=0&&2*a[j]-a[i]<=inf) ans+=(LL)g1[2*a[j]-a[i]];
if(2*a[i]-a[j]>=0&&2*a[i]-a[j]<=inf) ans+=(LL)g2[2*a[i]-a[j]];
}
g2[a[i]]++;
}
}
int main()
{
n=read();
for(int i=1;i<=n;i++) a[i]=read(),g1[a[i]]++;
for(int i=1;i<=n;i++)
{
pre[i]=i/len+1;
if(pre[i]!=pre[i-1]) L[pre[i]]=i,R[pre[i-1]]=i-1;
}
R[pre[n]]=n;
for(int i=1;i<=pre[n];i++) solve(L[i],R[i]);
printf("%lld\n",ans);
}
bzoj 2194
裸题不解释。
3771
大力容斥即可。
bzoj 4827
化下式子可以发现,c与ans是个二次函数关系,那么可以算出卷积后枚举c得到最优解。
于是将上面的式子倍长,下面的式子翻转,做fft即可。
bzoj 4259/bzoj 4503
好题,首先有一个暴力点的做法,首先将所有a标为1,其它为0,做卷积 ,然后将所有b标为1,其它为0,在做卷积……将答案累加起来,看一下某一位的值是否是匹配串的长度即可。
按这样稳稳的T掉,于是定义两个字符的差异为:
s[i]∗t[i]∗(s[i]−t[i])2
s
[
i
]
∗
t
[
i
]
∗
(
s
[
i
]
−
t
[
i
]
)
2
,?看作0。
显然只有当两个字符一样时,或者有?时,才会是0,而且这个式子是可以卷积的。
code(4259):
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
const double pi=acos(-1),eps=1e-8;
struct P{
double x,y;
P() {x=y=0;}
P(double a,double b) {x=a;y=b;}
}a[1200000],b[1200000],c[1200000],ans[1200000];
P operator+(P x,P y) {return P(x.x+y.x,x.y+y.y);}
P operator-(P x,P y) {return P(x.x-y.x,x.y-y.y);}
P operator*(P x,P y) {return P(x.x*y.x-x.y*y.y,x.y*y.x+x.x*y.y);}
int bin[1200000],len1,len2;
char S[300000],T[300000];
int s[300000],t[300000];
void fft(P *a,int n,int op)
{
for(int i=0;i<n;i++) bin[i]=(bin[i>>1]>>1)|((i&1)*(n>>1));
for(int i=0;i<n;i++) if(i<bin[i]) swap(a[i],a[bin[i]]);
for(int i=1;i<n;i<<=1)
{
P wn=P(cos(pi/i),sin(pi/i)*op),t,w;
for(int j=0;j<n;j+=i<<1)
{
w=P(1,0);
for(int k=0;k<i;k++)
{
t=a[j+i+k]*w;w=w*wn;
a[i+j+k]=a[j+k]-t;a[j+k]=a[j+k]+t;
}
}
}
if(op==-1) for(int i=0;i<n;i++) a[i].x/=n;
}
int n=1;
int num=0,list[100010];
void print(P *a,int n){for(int i=0;i<n;i++) printf("%0.lf ",a[i].x);printf("\n");}
int main()
{
scanf("%d %d",&len2,&len1);
scanf("%s %s",T+1,S+1);
//len1=strlen(S+1);len2=strlen(T+1);
for(int i=1;i<=len1;i++) s[i]=S[i]=='*'?0:S[i]-'a'+1;
for(int i=1;i<=len2;i++) t[i]=T[i]=='*'?0:T[i]-'a'+1;
while(n<=len1+len2) n<<=1;
for(int i=1;i<=len2;i++) a[len2-i+1].x=t[i]*t[i]*t[i];
for(int i=1;i<=len1;i++) b[i].x=s[i];
//print(a,n);print(b,n);
fft(a,n,1);fft(b,n,1);
for(int i=0;i<n;i++) ans[i]=ans[i]+a[i]*b[i];
for(int i=0;i<n;i++) a[i].x=a[i].y=b[i].x=b[i].y=0;
for(int i=1;i<=len2;i++) a[len2-i+1].x=-2*t[i]*t[i];
for(int i=1;i<=len1;i++) b[i].x=s[i]*s[i];
fft(a,n,1);fft(b,n,1);
for(int i=0;i<n;i++) ans[i]=ans[i]+a[i]*b[i];
for(int i=0;i<n;i++) a[i].x=a[i].y=b[i].x=b[i].y=0;
for(int i=1;i<=len2;i++) a[len2-i+1].x=t[i];
for(int i=1;i<=len1;i++) b[i].x=s[i]*s[i]*s[i];
fft(a,n,1);fft(b,n,1);
for(int i=0;i<n;i++) ans[i]=ans[i]+a[i]*b[i];
fft(ans,n,-1);
for(int i=1;i<=len1-len2+1;i++) if(ans[i+len2].x<0.5) list[++num]=i;
printf("%d\n",num);
for(int i=1;i<=num;i++) printf("%d ",list[i]);
}
bzoj 4332
显然可以写出一个转移多项式g。
那这题相当于求
f(g+g2+g3+g4+……+gn)
f
(
g
+
g
2
+
g
3
+
g
4
+
…
…
+
g
n
)
分治即可。