• BZOJ3509. [CodeChef] COUNTARI


    传送门

    变一下题目的式子,变成 $A[i]+A[k]=2A[j],i<j,k>j$

    发现 $A[i]$ 的值域不大,考虑移动指针 $pos$ 并维护 $cntl[],cntr[]$ 分别表示 $pos$ 左右两边各种值的数的数量

    设 $ans[i]$ 表示当前 $pos$ 左右两边各取一个数,相加为 $i$ 的方案数,那么对于每一个 $j=pos$,贡献即为 $ans[2A[pos]]$

    同时 $ans[i]=sum_{j=0}^{i}cntl[j]cntr[i-j]$,其实就是一个卷积的形式,可以用 $FFT$ 优化,但是 $ans$ 会随着 $pos$ 改变一起改变

    如果用 $FFT$ 未免大材小用了,对于每一个位置 $pos$ 我们只想知道 $ans[2A[pos]]$,其他的都无关,$FFT$ 好像只会白白增加复杂度

    考虑分块,设块大小为 $T$

    对于块内的情况可以直接暴力算,复杂度 $nT$

    对于 $i$ 在块左边,$j$ 在块内,$k$ 在块右边的情况,直接搞 $FFT$

    这样一次 $FFT$ 可以解决 块大小的答案,设值域为 $S$,那么这样复杂度就是 $O(n/TSlog_S)$

    发现 $T$ 取 $sqrt(n)$ 时最优,但是因为 $FFT$ 常数大,所以可以适当增大 $T$ 来减少 $FFT$ 的次数

    具体看代码就行,不难看懂

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    typedef double db;
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=1e5+7;
    const db pi=acos(-1.0);
    struct CP {
        db x,y;
        CP (db xx=0,db yy=0) { x=xx,y=yy; }
        inline CP operator + (const CP &tmp) const {
            return CP(x+tmp.x,y+tmp.y);
        }
        inline CP operator - (const CP &tmp) const {
            return CP(x-tmp.x,y-tmp.y);
        }
        inline CP operator * (const CP &tmp) const {
            return CP(x*tmp.x-y*tmp.y,x*tmp.y+y*tmp.x);
        }
    }A[N],B[N];
    int n,p[N],T,d[N],cntl[N],cntr[N];
    ll ans;
    void FFT(CP *A,int len,int type)
    {
        for(int i=0;i<len;i++) if(i<p[i]) swap(A[i],A[p[i]]);
        for(int mid=1;mid<len;mid<<=1)
        {
            CP wn(cos(pi/mid),type*sin(pi/mid));
            for(int R=mid<<1,j=0;j<len;j+=R)
            {
                CP w(1,0);
                for(int k=0;k<mid;k++,w=w*wn)
                {
                    CP x=A[j+k],y=w*A[j+mid+k];
                    A[j+k]=x+y;
                    A[j+mid+k]=x-y;
                }
            }
        }
    }
    int main()
    {
        n=read(); T=min(n,6*(int)sqrt(n));
        for(int i=1;i<=n;i++) d[i]=read(),cntr[d[i]]++;
        for(int i=1;i<n;i+=T)
        {
            int r=min(i+T-1,n);
            for(int j=i;j<=r;j++) cntr[d[j]]--;
            for(int j=i;j<=r;j++)
            {
                for(int k=j+1;k<=r;k++)
                {
                    int t=(d[j]<<1)-d[k]; if(t>=0) ans+=cntl[t];
                    t=(d[k]<<1)-d[j]; if(t>=0) ans+=cntr[t];
                }
                cntl[d[j]]++;
            }
        }
        for(int i=1;i<n;i+=T)
        {
            int mx=0,r=min(i+T-1,n);
            for(int j=1;j<i;j++) A[d[j]].x++,mx=max(mx,d[j]);
            for(int j=r+1;j<=n;j++) B[d[j]].x++,mx=max(mx,d[j]);
            int len=1,tot=0;
            while(len<=mx*2) len<<=1,tot++;
            for(int j=0;j<len;j++) p[j]=(p[j>>1]>>1) | ((j&1)<<(tot-1));
            FFT(A,len,1); FFT(B,len,1);
            for(int j=0;j<=len;j++) A[j]=A[j]*B[j];
            FFT(A,len,-1);
            for(int j=i;j<=r;j++) ans+=ll(A[d[j]<<1].x/len+0.5);
            for(int j=0;j<=len;j++) A[j]=B[j]=CP(0,0);
        }
        printf("%lld
    ",ans);
        return 0;
    }
  • 相关阅读:
    HTML元素解释
    Java命名规范
    HDU 1058 Humble Numbers(DP,数)
    HDU 2845 Beans(DP,最大不连续和)
    HDU 2830 Matrix Swapping II (DP,最大全1矩阵)
    HDU 2870 Largest Submatrix(DP)
    HDU 1421 搬寝室(DP)
    HDU 2844 Coins (组合背包)
    HDU 2577 How to Type(模拟)
    HDU 2159 FATE(二维完全背包)
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/11254861.html
Copyright © 2020-2023  润新知