【NOIP2016——PJ4】魔法阵

传送门:http://codevs.cn/problem/5624/

算法一(送分算法):

最朴素的算法应该就是四重循环暴力枚举每一个 xa,xb,xc,xd
当然只能通过较少的点
于是 O(n4)

算法二(骗分算法):

由算法一改变一下便可以省下一重循环
暴力出 xa,xb,xc,xd
于是 O(n3)
当然还可以加一些常数上的优化 比如确定 xa,xbxc 其实范围已经缩小了

算法三(似乎可以AC):

可以算是一个折半枚举吧
用 ans[x][y] 记录 数x(注意不是第x个)在位置y上出现的次数
那么枚举差值 i ,
再枚举 xa
则 :
xb=xa+i2;
xc>xb+i6=xa+i8;
xd=xc+i;
换句话说 xb 已确定下来
然后再来研究一下 xc,xd
xc 确定下来后, xd 也确定下来
并且随着 xa 的增大 xc 的范围单调递减(反过来就是随着 xa 的增大, xc 的范围单调递增)
于是就可以利用这一性质求出 sum=(f[xc]f[xd])
然后就能累加到 ans[xa][1]ans[xb][2]
同理累加 ans[xc][3]ans[xd][4]
详见代码

#include
#include
#include

using namespace std;

int n,m,sum,A,B,C,D,a[40005],f[20005],ans[20005][5];

int main(){
    cin>>n>>m;
    for (int i=1;i<=m;i++){
        cin>>a[i];
        f[a[i]]++;
    }

    for (int i=1;i<=2000;i++){
        sum=0;
        for (A=n-9*i-1;A>=1;A--){
            B=A+2*i; C=A+8*i+1; D=A+9*i+1;
            sum+=f[C]*f[D];
            ans[A][1]+=f[B]*sum;
            ans[B][2]+=f[A]*sum;
        }

        sum=0;
        for (C=2+8*i;C<=n;C++){
            A=C-8*i-1; B=C-6*i-1; D=C+i;
            sum+=f[A]*f[B];
            ans[C][3]+=f[D]*sum;
            ans[D][4]+=f[C]*sum;
        }
    }

    for (int i=1;i<=m;i++) 
        cout<1]<<' '<2]<<' '<3]<<' '<4]<<'\n';

    return 0;
}

你可能感兴趣的:(枚举)