FFT模板 & 瞎做

本文介绍了快速傅里叶变换(FFT)在多项式乘法中的应用,通过实例展示了如何将不同形式的求和问题转化为标准卷积,从而利用FFT求解,包括Luogu P3803、CF Gym101002E等题目。总结了将次数之和或之差为定值的求和形式转化为标准卷积的技巧。
摘要由CSDN通过智能技术生成

参考资料

  1. 快速傅里叶变换(FFT)详解
  2. FFT 学习笔记
  3. FFT模板以及简单的FFT入门题总结

模板

typedef cp complex<double>;
namespace fft
{
    cp o[4*MAXN],oi[4*MAXN];
    void init(int n)
    {
        for(int i=0;i<n;i++)
        {
            o[i] =cp(cos(2*PI/n*i),sin(2*PI/n*i));
            oi[i]=conj(o[i]);
        }
    }
    void transform(cp a[],const int n,cp o[])
    {
        //二进制翻转
        int k=0;
        while((1<<k)<n)k++;
        for(int i=0;i<n;i++)
        {
            int t=0;
            for(int j=0;j<k;j++)
                if(i&(1<<j))
                    t|=(1<<(k-j-1));
            if(i<t) swap(a[i],a[t]);
        }
        for(int l=2;l<=n;l*=2)
        {
            int m=l/2;
            //将两个长度为 m 的序列的答案合并为长度为 l 的序列的答案
            for(cp *p=a;p!=a+n;p+=l)
            {
                for(int i=0;i<m;i++)
                {
                    cp t=o[n/l*i]*p[m+i];
                    p[m+i]=p[i]-t,p[i]+=t;
                }
            }
        }
    }
    void dft (cp a[],int n){transform(a,n,o);}
    void idft(cp a[],int n){transform(a,n,oi);for(int i=0;i<n;i++) a[i]/=n;}
};

//n1 n2为多项式a1 a2系数的个数 不是最高次数(注意+1)

void mul(int a1[],int n1,int a2[],int n2,int res[])
{
    int n=1;
    while(n<n1+n2) n=n<<1;
    static cp c1[4*MAXN],c2[4*MAXN];
    for(int i=0;i< n;i++) c1[i]=c2[i]=0;
    for(int i=0;i<n1;i++) c1[i].real(a1[i]);
    for(int i=0;i<n2;i++) c2[i].real(a2[i]);
    fft::init(n);
    fft::dft(c1,n),fft::dft(c2,n);
    for(int i=0;i<n;i++) c1[i]*=c2[i];
    fft::idft(c1,n);
    for(int i=0;i<n1+n2-1;i++)
        res[i]=int(floor(c1[i].real()+0.5));
}


/*
void mul(double a1[],int n1,double a2[],int n2,double res[])
{
    int n=1;
    while(n<n1+n2) n=n<<1;
    static cp c1[4*MAXN],c2[4*MAXN];
    for(int i=0;i< n;i++) c1[i]=c2[i]=0;
    for(int i=0;i<n1;i++) c1[i].real(a1[i]);
    for(int i=0;i<n2;i++) c2[i].real(a2[i]);
    fft::init(n);
    fft::dft(c1,n),fft::dft(c2,n);
    for(int i=0;i<n;i++) c1[i]*=c2[i];
    fft::idft(c1,n);
    for(int i=0;i<n1+n2-1;i++)
        res[i]=c1[i].real();
}
*/

常数优化

struct cp
{
    double r,i;
    cp(double _r = 0,double _i = 0){r = _r; i = _i;}
    cp operator +(const cp  &b){return cp(r+b.r,i+b.i);}
    cp operator -(const cp  &b){return cp(r-b.r,i-b.i);}
    cp operator *(const cp  &b){return cp(r*b.r-i*b.i,r*b.i+i*b.r);}
    cp operator /(const int &n){return cp(r/n,i/n);}
};

namespace fft
{
    int R[MAXN*4];
    void init(int n)
    {
        int L=0;
        while((1<<L)<n) L++;
        for(int i=0;i<n;i++)
            R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
    }
    void transform(cp a[],const int n,int f)
    {
        for(int i=0;i<n;i++)
            if(i<R[i])
                swap(a[i],a[R[i]]);
        for(int l=2;l<=n;l=l<<1)
        {
            int m=l/2;
            cp wn=cp(cos(2*PI/l),f*sin(2*PI/l));
            for(cp *p=a;p!=a+n;p+=l)
            {
                cp w=cp(1,0);
                for(int i=0;i<m;i++,w=w*wn)
                {
                    cp t=w*p[m+i];
                    p[m+i]=p[i]-t,p[i]=p[i]+t;
                }
            }
        }
    }
    void dft (cp a[],int n){transform(a,n, 1);}
    void idft(cp a[],int n){transform(a,n,-1);for(int i=0;i<n;i++) a[i]=a[i]/n;}
};

cp c1[MAXN*4],c2[MAXN*4];
void mul(LL a1[],int n1,LL a2[],int n2,LL res[])
{
    int n=1;
    while(n<n1+n2) n=n<<1;
    for(int i=0;i<=n;i++) c1[i]=c2[i]=cp(0,0);
    for(int i=0;i<n1;i++) c1[i]=cp(a1[i],0);
    for(int i=0;i<n2;i++) c2[i]=cp(a2[i],0);
    fft::init(n);
    fft::dft(c1,n),fft::dft(c2,n);
    for(int i=0;i<n;i++) c1[i]=c1[i]*c2[i];
    fft::idft(c1,n);
    for(int i=0;i<n1+n2-1;i++)
        res[i]=LL(floor(c1[i].r+0.5));
}

FFT瞎做 & 简要题解
1.luogu P3803 【模板】多项式乘法(FFT)
贴板子AC一气呵成,,,

2.CF Gym101002E E - K-Inversions
下标差的绝对值为K的一对字符A和B称为K逆序对,依次输出字符串中1~n-1逆序对的个数。

万恶之源,Wannafly Camp Day3开的比赛的E题,讲题时dls说这是FFT板子题一眼秒了,,,

构造两个函数
f ( x ) = { 1 , str[x]==’A’ 0 , else g ( x ) = { 1 , str[x]==’B’ 0 , else f(x)= \begin{cases} 1, &amp; \text {str[x]==&#x27;A&#x27;} \\ 0, &amp; \text{else} \end{cases} g(x)= \begin{cases} 1, &amp; \text {str[x]==&#x27;B&#x27;} \\ 0, &amp; \text{else} \end{cases} f(x)={1,0,str[x]==’A’elseg(x)={1,0,str[x]==’B’else
问题等价于求函数
h ( j ) = ∑ i = j + 1 n f ( i ) ∗ g ( i − j ) h(j)=\sum_{i=j+1}^{n}f(i)*g(i-j) h(j)=i=j+1nf(i)g(ij)
在1~n-1上的取值
设函数 g ′ ( x ) g&#x27;(x) g(x)是由 g ( x ) g(x) g(x)翻转得到的(字符串下标由1开始),即:
g ′ ( n − x + 1 ) = g ( x ) g&#x27;(n-x+1)=g(x) g(nx+1)=g(x)
原式变为
h ( j ) = ∑ i = j + 1 n f ( i ) ∗ g ′ ( n − i + j + 1 ) h(j)=\sum_{i=j+1}^{n}f(i)*g&#x27;(n-i+j+1) h(j)=i=j+1nf(i)g(ni+j+1)
注意到 g ( n − i + j + 1 ) g(n-i+j+1) g(ni+j+1) f ( i ) f(i) f(i)的自变量之和等于 n + j + 1 n+j+1 n+j+1,有点类似于我们熟悉的卷积,考虑扩展上下界至 [ 0 , n + j + 1 ] [0, n+j+1] [0,n+j+1]

因为 f ( i ) = 0 , i ∈ [ n + 1 , n + j + 1 ] f(i)=0,i\in[n+1,n+j+1] f(i)=0,i[n+1,n+j+1]
g ( n − i + j + 1 ) = 0 , i ∈ [ 0 , j ] g(n-i+j+1)=0,i\in[0,j] g(ni+j+1)=0,i[0,j]
所以
h ( j ) = ∑ i = 0 n + j + 1 f ( i ) ∗ g ′ ( n − i + j + 1 ) h(j)=\sum_{i=0}^{n+j+1}f(i)*g&#x27;(n-i+j+1) h(j)=i=0n+j+1f(i)g(ni+j+1)
f ( x ) f(x) f(x) g ′ ( x ) g&#x27;(x) g(x)写成多项式后相乘, x n + j + 1 x^{n+j+1} xn+j+1项的系数就是 h ( j ) h(j) h(j)的值,FFT板子贴上搞一搞就行。

int n;
int a[MAXN],b[MAXN],res[MAXN];
char s[MAXN];

int work()
{
    scanf("%s",s+1);
    n=strlen(s+1);
    for(int i=1;i<=n;i++) a[i]=(s[i]=='A'),b[n-i+1]=(s[i]=='B');
    mul(a,n+1,b,n+1,res);
    for(int j=1;j<n;j++)
        printf("%d\n",res[n+j+1]);
    return 0;
}

3.BZOJ2194 快速傅立叶之二

定义 C k = ∑ i = k n − 1 A i B i − k C_{k}=\sum_{i=k}^{n-1} A_{i}B_{i-k} Ck=i=kn1AiBik ,分别求 C 0 C_{0} C0 ~ C n − 1 C_{n-1} Cn1 的值。

设数组 B ′ B&#x27; B由数组 B B B翻转所得,即:
B n − i − 1 ′ = B i B&#x27;_{n-i-1}=B_{i} Bni1=Bi
所以
C k = ∑ i = k n − 1 A i B n − i + k − 1 ′ C_{k}=\sum_{i=k}^{n-1} A_{i}B&#x27;_{n-i+k-1} Ck=i=kn1AiBni+k1

因为
B n − i + k + 1 ′ = 0 , i ∈ [ 0 , k − 1 ] B&#x27;_{n-i+k+1}=0,i\in[0,k-1] Bni+k+1=0,i[0,k1]
A i = 0 , i ∈ [ n , n + k − 1 ] A_{i}=0,i\in[n,n+k-1] Ai=0,i[n,n+k1]

所以可将上下界拓展至 [ 0 , n + k − 1 ] [0,n+k-1] [0,n+k1],即:
C k = D n + k − 1 = ∑ i = 0 n + k − 1 A i B n − i + k − 1 ′ C_{k}=D_{n+k-1}=\sum_{i=0}^{n+k-1} A_{i}B&#x27;_{n-i+k-1} Ck=Dn+k1=i=0n+k1AiBni+k1
A i A_{i} Ai B i ′ B&#x27;_{i} Bi 作为两个多项式的系数,相乘后得到的 x n + k − 1 x^{n+k-1} xn+k1项的系数即为所求的 C k C_{k} Ck

int n;
int a[MAXN],b[MAXN],res[MAXN];
 
int work()
{
    n=read();
    for(int i=0;i<n;i++)
        a[i]=read(),b[n-i-1]=read();
    mul(a,n,b,n,res);
    for(int i=0;i<n;i++)
        printf("%d\n",res[n+i-1]);
    return 0;
}

小结:形如 C j = ∑ i = 0 j A i B j − i C_{j}=\sum_{i=0}^{j} A_{i}B_{j-i} Cj=i=0jAiBji 次数之和为定值的形式为标准的卷积,但次数之差为定值的形式可以通过翻转其中一个数组并扩展上下界来化为标准的卷积形式。

4.BZOJ3527 [Zjoi2014]力
定义
E j = ∑ i = 1 j − 1 q i ( i − j ) 2 − ∑ i = j + 1 n q i ( i − j ) 2 E_{j}=\sum_{i=1}^{j-1}\frac{q_{i}}{(i-j)^2}-\sum_{i=j+1}^{n}\frac{q_{i}}{(i-j)^2} Ej=i=1j1(ij)2qii=j+1n(ij)2qi
E 1 E{_1} E1 ~ E n E_{n} En的值。

将两个求和部分分开考虑,定义
f ( x ) = { q x 1 &lt; = x &lt; = n 0 x = 0 g ( x ) = { 1 x 2 x ≠ 0 0 x = 0 f(x)= \begin{cases} q_{x} &amp; 1&lt;=x&lt;=n \\ 0 &amp; x=0 \end{cases} g(x)=\begin{cases} \frac{1}{x^2} &amp; {x} \neq 0 \\ {0} &amp; x=0 \end{cases} f(x)={qx01<=x<=nx=0g(x)={x210x̸=0x=0
∑ i = 1 j − 1 q i ( i − j ) 2 \sum_{i=1}^{j-1}\frac{q_{i}}{(i-j)^2} i=1j1(ij)2qi就可以写作
∑ i = 1 j − 1 f ( i ) g ( i − j ) \sum_{i=1}^{j-1}f(i)g(i-j) i=1j1f(i)g(ij)

因为 f ( 0 ) = 0 f(0)=0 f(0)=0 并且 g ( j − j ) = g ( 0 ) = 0 g(j-j)=g(0)=0 g(jj)=g(0)=0,所以可将上下界扩展至 [ 0 , j ] [0,j] [0,j],标准的卷积形式
∑ i = 0 j f ( i ) g ( i − j ) \sum_{i=0}^{j}f(i)g(i-j) i=0jf(i)g(ij)
第二部分可以通过第二/第三题的翻转序列并扩展上下界技巧转换为标准卷积形式,两部分分别FFT并相减即可。

int n;
double q[MAXN],p[MAXN];
double res1[MAXN],res2[MAXN];
 
int work()
{
    cin >> n;
    for(int i=1;i<=n;i++) cin >> q[i];
    for(int i=1;i<=n;i++) p[i]=(1.0/(double(i)*double(i)));
    mul(q,n+1,p,n+1,res1);
    for(int i=1;i<=(n/2);i++) swap(p[i],p[n-i+1]);
    mul(q,n+1,p,n+1,res2);
    for(int i=1;i<=n;i++)
        printf("%f\n",res1[i]-res2[n+i+1]);
    return 0;
}

5.HDU4609 3-idiots
给你一个包含n个数的数列 a 1 a_{1} a1 ~ a n a_{n} an,求任选三个数构成三角形的概率(方案)。

构造一个多项式, A i A_{i} Ai x i x^i xi 的系数,并且 A i A_{i} Ai等于数列中 i i i 出现的次数,将这个多项式和自己乘一下,所得新的多项式 x i x^{i} xi 的系数 B i B_{i} Bi 即为数列中选两个数和为 i i i 的方案数。但题目要求选的两个数不能相同,因此对于每一个 a i a_{i} ai 都要在 B 2 ∗ a i B_{2*a_{i}} B2ai 中减去选两个 a i a_{i} ai 的方案。此外 A i A_{i} Ai 所包含的方案中 a i + a j a_{i}+a_{j} ai+aj a j + a i a_{j} + a_{i} aj+ai 被视为两种不同的方案(因为在多项式相乘中 x a i + a j x^{a_{i}+a_{j}} xai+aj的系数 B a i + a j = A a i A a j + A a j A a i ) B_{a_{i}+a_{j}}=A_{a_{i}}A_{a_{j}}+A_{a_{j}}A_{a_{i}}) Bai+aj=AaiAaj+AajAai),因此除以二后便得到了任选两个不相同的数构成 i i i 的方案数 B i B_{i} Bi
考虑枚举三角形的最大边来计算所有不能构成三角形的方案数量,因为没有负数和0,不合法方案中的较小的两条边之和一定小于等于第三边,因此枚举最大边的长度 i i i 并统计两边之和 &lt; = i &lt;=i <=i的方案数即可(对系数 B i B_{i} Bi做前缀和)。
数组开小交了20多发

LL n,m,p,q;
int x[MAXN];
LL num[N],res[N];

void init()
{
    p=q=n*(n-1)*(n-2)/6,m=0;
    mst(num,0);
}

int work()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        init();
        for(int i=1;i<=n;i++)
            scanf("%d",&x[i]),num[x[i]]++,m=max(m,(LL)x[i]);
        mul(num,m+1,num,m+1,res);
        for(int i=1;i<=n  ;i++) res[x[i]*2]--;
        for(int i=1;i<=2*m;i++) res[i]/=2,res[i]+=res[i-1];
        for(int i=1;i<=2*m;i++) p-=res[i]*num[i];
        printf("%.7lf\n",(double)p/q);
    }
    return 0;
}

正向考虑似乎有点麻烦,可以参考kuangbin大大的博客,,,

6.UVA - 12298 Super Poker II
现在有一副只有合数(没有1)的扑克牌,写着某个数的扑克牌有四种(Spade Heart Club Diamond)且各有一张,现在有一些扑克牌丢了,求由四种扑克牌构成 [ a , b ] [a,b] [a,b] 上每一个数的方案数。
和上一题的思想类似,构造四个多项式分别代表四种扑克牌,若某一种数字为 x x x 的扑克牌存在就在对应的多项式系数 A x A_{x} Ax上让它等于 1 1 1,三次多项式乘法后输出次数为 [ a , b ] [a,b] [a,b] 项的系数就行。
注意开long double 有点卡精度
上午交的下午才看到结果 神仙UVA评测姬

map<char,int> h;
int l,r,m;
LL  num[4][MAXN];
void pre()
{
    for(int i=1;i<=r;i++)
    {
        bool flag=0;
        for(int j=2;!flag&&j<i;j++)
            if(i%j==0)
                flag=1;
        for(int k=0;k<4;k++)
            num[k][i]=flag;
    }
}

int work()
{
    h['S']=0,h['H']=1,h['C']=2,h['D']=3;
    while(cin >> l >> r >> m && l && r)
    {
        pre();
        while(m--)
        {
            string card;
            cin >> card;
            int t=0;
            for(auto ch:card)
                if(ch>='0'&&ch<='9')
                    t=t*10+ch-'0';
            num[h[card.back()]][t]--;
        }
        for(int k=1;k<4;k++)
            mul(num[0],r+1,num[k],r+1,num[0]);
        for(int i=l;i<=r;i++)
            cout << num[0][i] << endl;
        cout << endl;
    }
    return 0;
}

然后翻到了学长的博客,又补了几题,,,

7.2018宁夏网络赛H题 Rock Paper Scissors Lizard Spock

题意抄下学长的ORZ,,,

给你类似剪刀石头布游戏的五种手势和十种克制关系。每种手势克制其他两种手势并被另外两种手势克制。
给你两个字符串分别表示A,B的手势顺序,A长B短,你可以随意挪动B相对于A的位置,求B最多能赢多少次?

首先可以考虑字符串中某一种手势赢的次数,以P为例,P可以赢K和R,所以从位置 j j j 开始游戏能赢的次数等价于求函数:
h ( j ) = ∑ i = j j + m − 1 f ( i ) g ( i − j + 1 ) h(j)=\sum_{i=j}^{j+m-1}f(i)g(i-j+1) h(j)=i=jj+m1f(i)g(ij+1)
[ 1 , n ] [1,n] [1,n] 的最大值(字符串下标从1开始)。

其中
f ( x ) = { 1 , str1[x]=’K’ or str1[x]=’R’ 0 , else g ( x ) = { 1 , str2[x]=’P’ 0 , else f(x)= \begin{cases} 1, &amp; \text {str1[x]=&#x27;K&#x27; or str1[x]=&#x27;R&#x27;} \\ 0, &amp; \text{else} \end{cases} g(x)= \begin{cases} 1, &amp; \text {str2[x]=&#x27;P&#x27;} \\ 0, &amp; \text{else} \end{cases} f(x)={1,0,str1[x]=’K’ or str1[x]=’R’elseg(x)={1,0,str2[x]=’P’else

老套路,翻转 g ( x ) g(x) g(x) 扩展上下界:
g ′ ( m − x + 1 ) = g ( x ) g&#x27;(m-x+1)=g(x) g(mx+1)=g(x)
h ( j ) = ∑ i = j j + m − 1 f ( i ) g ′ ( m − i + j ) = ∑ i = 0 j + m f ( i ) g ′ ( m − i + j ) h(j)=\sum_{i=j}^{j+m-1}f(i)g&#x27;(m-i+j)=\sum_{i=0}^{j+m}f(i)g&#x27;(m-i+j) h(j)=i=jj+m1f(i)g(mi+j)=i=0j+mf(i)g(mi+j)

因为 f ( 0 ) = 0 , g ′ ( m − j − m + j ) = g ′ ( 0 ) = g ( m + 1 ) = 0 f(0)=0,g&#x27;(m-j-m+j)=g&#x27;(0)=g(m+1)=0 f(0)=0,g(mjm+j)=g(0)=g(m+1)=0

其他四种手势也类似,最后把 x m + j , j ∈ [ 1 , n ] x^{m+j} ,j\in[1,n] xm+j,j[1,n] 的系数累加取个max就是答案了。

int n,m;
char s1[MAXN],s2[MAXN];
int a[MAXN*2],b[MAXN*2],ans[MAXN];
map<char,vector<char>> h;
vector<char> type={'P','R','S','L','K'};

void pre()
{
    vector<char> t;
    t.clear(),t.pb('R'),t.pb('K'),h['P']=t; //P-R K
    t.clear(),t.pb('P'),t.pb('L'),h['S']=t; //S-P L
    t.clear(),t.pb('L'),t.pb('S'),h['R']=t; //R-L S
    t.clear(),t.pb('K'),t.pb('P'),h['L']=t; //L-K P
    t.clear(),t.pb('S'),t.pb('R'),h['K']=t; //K-S R
}

int work()
{
    pre();
    scanf("%s",s1+1);
    scanf("%s",s2+1);
    n=strlen(s1+1),m=strlen(s2+1);
    for(auto cur:type)
    {
        for(int i=1;i<=m;i++) b[m-i+1]=(s2[i]==cur);
        for(int i=1;i<=n;i++) a[i]=(find(all(h[cur]),s1[i])!=h[cur].end());
        mul(a,n+1,b,m+1,a);
        for(int j=1;j<=n;j++) ans[j]+=a[j+m];
    }
    int t=0;
    for(int j=1;j<=n;j++)
        t=max(t,ans[j]);
    return printf("%d\n",t);
}

8.BZOJ4259 残缺的字符串
题意中文的。。。大致就是带通配符的字符串匹配
定义
f ( x ) = { 0 , str1[x]=’*’ s t r 1 [ x ] , else g ( x ) = { 0 , str2[x]=’*’ s t r 2 [ x ] , else f(x)= \begin{cases} 0, &amp; \text {str1[x]=&#x27;*&#x27;} \\ str1[x], &amp; \text{else} \end{cases} g(x)= \begin{cases} 0, &amp; \text {str2[x]=&#x27;*&#x27;} \\ str2[x], &amp; \text{else} \end{cases} f(x)={0,str1[x],str1[x]=’*’elseg(x)={0,str2[x],str2[x]=’*’else
字符串 s t r 2 str2 str2在字符串 s t r 1 str1 str1 j j j 位置开始能匹配上等价于
h ( j ) = ∑ i = j j + m − 1 ( f ( i ) − g ( i − j + 1 ) ) 2 ∗ f ( i ) ∗ g ( i − j + 1 ) = 0 h(j)=\sum_{i=j}^{j+m-1}(f(i)-g(i-j+1))^{2}*f(i)*g(i-j+1) =0 h(j)=i=jj+m1(f(i)g(ij+1))2f(i)g(ij+1)=0
展开
h ( j ) = ∑ i = j j + m − 1 f ( i ) 3 ∗ g ( i − j + 1 ) − 2 ∗ ∑ i = j j + m − 1 f ( i ) 2 ∗ g ( i − j + 1 ) 2 + ∑ i = j j + m − 1 f ( i ) ∗ g ( i − j + 1 ) 3 h(j)=\sum_{i=j}^{j+m-1}f(i)^3*g(i-j+1) - 2*\sum_{i=j}^{j+m-1}f(i)^2*g(i-j+1)^2 + \sum_{i=j}^{j+m-1}f(i)*g(i-j+1)^3 h(j)=i=jj+m1f(i)3g(ij+1)2i=jj+m1f(i)2g(ij+1)2+i=jj+m1f(i)g(ij+1)3
三项分别把 g ( x ) g(x) g(x) 翻转,求卷积,合并,看下哪位是0就行。

这题卡常,,,抄板子请用下头那个

int n,m;
char s1[MAXN],s2[MAXN];
LL a[MAXN*4],b[MAXN*4],ans[MAXN];
LL f[MAXN],g[MAXN];
int work()
{
    scanf("%d%d",&m,&n);
    scanf("%s",s2+1);
    scanf("%s",s1+1);
    for(int i=1;i<=n;i++) f[i]=((s1[i]=='*')?0:(s1[i]-'a'+1));
    for(int i=1;i<=m;i++) g[i]=((s2[i]=='*')?0:(s2[i]-'a'+1));
 
    for(int i=1;i<=n;i++) a[i]=(LL)f[i]*f[i]*f[i];
    for(int i=1;i<=m;i++) b[m-i+1]=(LL)g[i];
    mul(a,n+1,b,m+1,a);
    for(int i=1;i<=n-m+1;i++) ans[i]+=a[i+m];
 
    for(int i=1;i<=n;i++) a[i]=f[i]*f[i];
    for(int i=1;i<=m;i++) b[m-i+1]=g[i]*g[i];
    mul(a,n+1,b,m+1,a);
    for(int i=1;i<=n-m+1;i++) ans[i]-=2*a[i+m];
 
    for(int i=1;i<=n;i++) a[i]=f[i];
    for(int i=1;i<=m;i++) b[m-i+1]=g[i]*g[i]*g[i];
    mul(a,n+1,b,m+1,a);
    for(int i=1;i<=n-m+1;i++) ans[i]+=a[i+m];
 
    int cnt=0;
    for(int i=1;i<=n-m+1;i++) cnt+=(ans[i]==0);
    printf("%d\n",cnt);
    for(int i=1;i<=n-m+1;i++)
        if(ans[i]==0)
            write(i),putchar(' ');
    if(cnt) putchar('\n');
    return 0;
}

9.A+B Problem Kattis - aplusb
给定n个数,求 i i i j j j k k k两两不同的三元组 ( i , j , k ) (i,j,k) (i,j,k),使得 a i + a j = a k a_{i}+a_{j}=a_{k} ai+aj=ak成立的三元组的个数。
由于又负数和0,首先把所有数加上一个 N N N,并且把 0 0 0 拿出单独考虑。
然后就和和5题的思想类似,构造多项式使得 x i x^i xi 的系数 A i A_{i} Ai 为数列中 i i i 出现的次数,做乘法后减去选两个 a i a_{i} ai 的方案,由于没有0 所以 a i a_{i} ai a j a_{j} aj a k a_{k} ak 一定两两不相同,枚举 a k a_{k} ak 统计答案。
包含0的话分三种情况讨论,选三个0,选两个相同数和一个0,选两个相反数和一个0,加上这些情况就行。

int n,m;
LL ans=0,num[MAXN],res[MAXN*2];
LL zero=0;
vector<int> s;

LL A(LL n,LL m)
{
    LL ans=1;
    for(LL i=0;i<m;i++)
        ans*=(n-i);
    return ans;
}

int work()
{
    n=read();
    for(int i=1;i<=n;i++)
    {
        int t=read()+N;
        if(t==N) zero++;
        else num[t]++,m=max(m,t),s.pb(t);
    }
    mul(num,m+1,num,m+1,res);
    for(auto x:s) res[x*2]--;
    //for(int i=2*N;i<=2*m;i++) ans+=num[i-N]*res[i];
    for(auto x:s) ans+=res[x+N];

    for(int x=0;x<=2*N;x++)
        if(num[x]>=2)
            ans+=num[x]*(num[x]-1)*zero*2;
    for(int x=N;x<=2*N;x++)
        if(num[x]&&num[2*N-x])
            ans+=num[x]*num[2*N-x]*2*zero;
    if(zero>=3) ans+=A(zero,3);

    write(ans),putchar('\n');
    return 0;
}

10 P1919 【模板】A*B Problem升级版 凑够十题

10 BZOJ3160 万径人踪灭

我已经想到了一个绝妙的解释,但写不下了,贴个题解跑路,,,

回炉重造.jpg
wtf

int manacher(int len,char s[],int p[])
{
    static char a[MAXN];
    int n=1,id=0,mx=0;
    mst(a,0),a[0]='$',a[1]='#';
    for(int i=1;i<=len;i++)
        a[++n]=s[i],a[++n]='#';
    a[n+1]='\0';
    for(int i=1;i<=n;i++)
    {
        if(mx>i) p[i]=min(p[2*id-i],mx-i-1);
        else p[i]=1;
        while(a[i+p[i]]==a[i-p[i]]) p[i]++;
        if(i+p[i]-1>mx) mx=i+p[i]-1,id=i;
    }
    return n;
}

int n;
LL a[MAXN],b[MAXN],res[MAXN];
int p[MAXN],f[MAXN];
LL  pw[MAXN],ans;
char s[MAXN];

const LL MOD = 1e9+7;

void solve(char c)
{
    for(int i=1;i<=n;i++) a[i]=(s[i]==c);
    for(int i=1;i<=n;i++) b[i]=(s[i]==c);
    mul(a,n+1,b,n+1,res);
    for(int i=0;i<=2*n;i++) f[i]+=res[i];
}

void init()
{
    pw[0]=1;
    for(int i=1;i<=2*n;i++)
        pw[i]=pw[i-1]*2%MOD;
}

int work()
{
    scanf("%s",s+1);
    n=strlen(s+1);
    solve('a'),solve('b');
    int len=manacher(n,s,p);
    init();
    for(int i=1;i<=len;i++)
        ans+=pw[(f[i]+1)/2]-1-p[i]/2,
        ans=((ans%MOD)+MOD)%MOD;
    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值