[NOIP2016]魔法阵

题目传送门


 暴力貌似能拿55,不过我没写 

读完题我们貌似可以推个式子:

∵b-a=2*(d-c)(这里我们用i来代替xi)

∴可以设d-c=i,则b-a=2*i

又∵b-a<(c-b)/3

∴2i<(c-b)/3

∴6i<c-b

同时∵不等式是<

∴6i+k=c-b (k>=1)

于是我们可以得到下图:

[NOIP2016]魔法阵

 对于85分做法,我们很容易想到:

我们可以利用三重循环:

  1. 枚举a的数值,范围:1->n
  2. 枚举i的数值,范围:1->(n-a-1)/9
  3. 枚举k的数值,范围:1->n-a-9*i

但是有一点需要注意,每一次发现合法魔法阵时

由于我们枚举的是魔法数值,于是若有相同魔法数值的两个魔法物品无法判断

所以我们在输入时就需要存一下每个魔法数值的魔法物品有多少个

然后每次加的时候用乘法原理处理一下就可以了

code-85:

#include<bits/stdc++.h>
using namespace std;
const int M=40010;
const int N=15010;
int n,m,b,c,d;
int x[M],ans[10][M],p[N];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&x[i]);
        p[x[i]]++;
    }
    for(int a=1;a<=n;a++)
    {
        if(p[a]==0) continue;
        for(int i=1;i<=(n-a-1)/9;i++)
        {
            b=a+2*i;
            if(p[b]==0) continue;
            for(int k=1;k<=n-a-9*i;k++)
            {
                c=b+6*i+k;
                if(p[c]==0) continue;
                d=c+i;
                if(p[d]==0) continue;
//                cout<<a<<' '<<b<<' '<<c<<' '<<d<<endl;
                ans[1][a]=ans[1][a]+p[b]*p[c]*p[d];
                ans[2][b]=ans[2][b]+p[a]*p[c]*p[d];
                ans[3][c]=ans[3][c]+p[a]*p[b]*p[d];
                ans[4][d]=ans[4][d]+p[a]*p[b]*p[c];
            }
        }
    }
    for(int i=1;i<=m;i++)
    {
        for(int j=1;j<=4;j++)
            printf("%d ",ans[j][x[i]]);
        puts("");
    }
    return 0;
}
Code-85 

然后我们发现这个算法的时间复杂度是O((n^3)/9)于是还是会爆

然后,在考场上就没有然后了……


 然后看了题解之后,就非常……

思路还是之前那样,求出了所有式子,因为循环三层会爆,所以我们就要尝试简化

由于k不确定,所以我们就不管它(迷惑

然后第一层循环i,第二层先循环d

由于c和d只差i,所以我们现在能知道c和d,再设k=1,便可以求出对应的a和b

如果我们将d右移之后得到了d'和c',同时又可以得到k=1的a'和b'

但是我们发现d',c',a,b同样可以组成合法的魔法阵,所以考虑利用类似前缀和的方法

定义sum为从d=9*i+2开始的所对应的的a,b的魔法阵数量和

再计算完c,d之后用同样的方法计算a,b即可

code-100:

#include<bits/stdc++.h>
using namespace std;
const int M=40010;
const int N=15010;
int n,m,a,b,c,d;
int x[M],ans[10][M],p[N];
inline int read()
{
    int f=1,x=0;char ch=getchar();
    while(ch<'0' || ch>'9') {if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0' && ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*f;
}
int main()
{
    n=read();m=read();
    for(int i=1;i<=m;i++)
    {
        x[i]=read();
        p[x[i]]++;
    }
    for(int i=1;i*9<n;i++)
    {
        int sum=0;
        for(d=i*9+2;d<=n;d++)
        {
            a=d-9*i-1;
            b=d-7*i-1;
            c=d-i;
            sum+=p[a]*p[b];
            ans[3][c]+=sum*p[d];
            ans[4][d]+=sum*p[c];
        }
        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+=p[c]*p[d];
            ans[1][a]+=sum*p[b];
            ans[2][b]+=sum*p[a];
        }
    }
    for(int i=1;i<=m;i++)
    {
        for(int j=1;j<=4;j++)
            printf("%d ",ans[j][x[i]]);
        puts("");
    }
    return 0;
}
Code-100