51nod 1690 区间求和2

题意

给出一个长度为n的数组a。区间[L,R]的值为 ∑R−Li=0a[L+i]∗a[R−i]
求所有长度为质数的区间的值的总和。

题解

很容易想到,枚举一个数对,然后统计他的答案
比如说,我们枚举了一个数对 (i,j) ( i , j )
那么他的答案的贡献会有两种情况
1. i+j<=n+1 i + j <= n + 1
这个的话,能包含他的区间长度是 [ji+1,ji+1+2(i1)] [ j − i + 1 , j − i + 1 + 2 ∗ ( i − 1 ) ]
化简一下式子可以的得到 [ji+1,j+i1] [ j − i + 1 , j + i − 1 ]
也就是他的答案的贡献是 a[i]a[j](s[i+j1]s[ji]) a [ i ] ∗ a [ j ] ∗ ( s [ i + j − 1 ] − s [ j − i ] )
s s 表示质数个数的前缀和
然后这个显然可以用FFT处理
然后对于i+j>n+1的情况也是同理
这里就不写了
有一个要注意的地方就是,你要保证 (i,j) ( i , j ) 合法,也就是要让 (i,j) ( i , j ) 同奇偶
在计算的时候,我们不把2当作质数
最后单独算就行
然后取模什么的不用管。。
最后模就行
用double会炸精度,所以要开long double
CODE:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
using namespace std;
typedef long long LL;
typedef long double LB;
const LB pi=M_PI;
const LL MOD=1e9+7;
const LL N=100005*10;
struct qq
{
    LB x,y;
    qq (){}
    qq (LB _x,LB _y)    {x=_x;y=_y;}
};
qq operator + (qq x,qq y)   {return qq(x.x+y.x, x.y+y.y);}
qq operator - (qq x,qq y)   {return qq(x.x-y.x, x.y-y.y);}
qq operator * (qq x,qq y)   {return qq(x.x*y.x-x.y*y.y, x.x*y.y+x.y*y.x);}
LL bin[N];
void fft (qq *a,LL n,LL o)
{
    for (LL u=0;u<n;u++) bin[u]=(bin[u>>1]>>1)|((u&1)*(n>>1));
    for (LL u=1;u<n;u++)
        if (u<bin[u])
            swap(a[u],a[bin[u]]);
    for (LL u=1;u<n;u<<=1)
    {
        qq wn=qq(cos(2*pi/(u<<1)),o*sin(2*pi/(u<<1)));
        for (LL i=0;i<n;i+=(u<<1))
        {
            qq w=qq(1,0);
            for (LL j=0;j<u;j++)
            {
                qq t=w*a[u+i+j];
                a[u+i+j]=a[i+j]-t;
                a[i+j]=a[i+j]+t;
                w=w*wn;
            }
        }
    }
}
LL n;
LL f[N];
LL pri[N],tot;
bool ok[N];
void prepare ()
{
    memset(ok,true,sizeof(ok));ok[1]=false;
    for (LL u=2;u<=n;u++)
    {
        if (ok[u])  pri[++tot]=u;
        for (LL i=1;i<=tot;i++)
        {
            LL j=pri[i];
            if (j*u>n) break;
            ok[j*u]=false;
            if (u%j==0) break;
        }
    }
    ok[2]=false;
    for (LL u=1;u<=n;u++) f[u]=f[u-1]+ok[u];
}
LL ans=0;
LL a[N];
qq b[N],c[N];
LL g[N];//储存所有i+j=k的s[i]*s[j] 
void solve ()//统计情况 
{
    memset(b,0,sizeof(b));memset(c,0,sizeof(c));
    LL nn=n*2,now;
    now=1;while (now<=nn) now<<=1; now<<=1;
    //for (LL u=0;u<now;u++) b[u]=c[u]=qq(0,0);
    for (LL u=1;u<=n;u++) b[n+u].x=a[u];
    for (LL u=1;u<=n;u++) c[n-u].x=a[u];

    fft(b,now,1);fft(c,now,1);
    for (LL u=0;u<now;u++) b[u]=b[u]*c[u];
    fft(b,now,-1);
    for (LL u=0;u<=n;u++)   
        if (u%2==0)
            ans=(ans-(f[u]*(LL)(b[u+nn].x/now+0.5))%MOD)%MOD;
    if (ans<0) ans+=MOD;
    ans=ans*2%MOD;
    //printf("%lld\n",ans);
    /*i+j<=n+1*/
    memset(b,0,sizeof(b));memset(c,0,sizeof(c));
    for (LL u=1;u<=n;u++) b[u].x=a[u];
    for (LL u=1;u<=n;u++) c[u].x=a[u];
    now=1;while (now<=nn) now<<=1;
    fft(b,now,1);fft(c,now,1);
    for (LL u=0;u<now;u++) b[u]=b[u]*c[u];
    fft(b,now,-1);
    for (LL u=0;u<=nn;u++) g[u]=(LL)(b[u].x/now+0.5);
    for (LL u=1;u<=n+1;u++)//i+j=u
        if (u%2==0)
            ans=(ans+g[u]*f[u-1]%MOD)%MOD;
    for (LL u=n+1;u<=nn;u++)
        if (u%2==0)
            ans=(ans+g[u]*f[nn+1-u]%MOD)%MOD;
}
int main()
{
    //freopen("a.in","r",stdin);
    scanf("%lld",&n);prepare();
    /*for (LL u=1;u<=n;u++) printf("%lld ",f[u]);
    printf("\n");*/
    for (LL u=1;u<=n;u++) scanf("%lld",&a[u]);
    solve();
    for (LL u=1;u<n;u++)    ans=(ans+a[u]*a[u+1]*2%MOD)%MOD;
    printf("%lld\n",ans);
    return 0;
}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值