(ps:我知道2017年来写NOIP2016的题解好像是蒟蒻干的事情,不过该写的还是要写的=_=|||)
给出m个物品和魔法值的最大值n,第i个物品魔法值为Xi(1<=Xi<=n),其中如果存在Xa<Xb<Xc<Xd,并且满足以下两个条件,则称这四个物品构成了一个魔法阵:
1.Xb-Xa=2(Xd-Xc)
2.Xb-Xa<(Xc-Xb)÷3
求每个物品作为魔法阵的a,b,c,d物品的次数。
【解法一】
m^4枚举,限制枚举范围,减少枚举次数。
期望得分:75分
【解法二】
n^3枚举,枚举前3个,推出第4个,限制枚举范围,减少枚举次数。
期望得分:85分
【解法三】
先n^2枚举,提前预处理sum[i][j]表示>=i,差值为j的二元组有几个,然后n^2枚举前2个,用sum直接得到个数。限制枚举范围,减少枚举次数。但空间无法承受。
期望得分:95分
【解法四】(标准解法)
由于【解法三】空间无法承受,但时间复杂度良好,不禁让我们去想解法三的优化。如果sum没有后面一维,那么完全承受得住。题目中的两个条件让我们发现Xa,Xb和Xc,Xd是独立的,这两个唯一的联系就是差值(记j=Xd-Xc,这里不用Xb-Xa的原因是Xb-Xa必须是偶数,太麻烦),那么完全可以枚举差值,然后直接拆开Xa,Xb和Xc,Xd,分开来做,就可以减少sum后面差值的那一维!
示意图:(后面Xa,Xb,Xc,Xd简写为a,b,c,d;ha[i]表示i的个数;num[i][0~3]表示i作为a,b,c,d的答案个数)
(b为a+2*j,c至少为a+8*j+1,d至少为a+9*j+1)
1.处理1,2
枚举差值之后,sum定义不变,sum[i]表示>=i,差值为j的二元组有几个。那么只需要O(n)的时间我们就可以处理出sum[i]。处理完毕之后,枚举a的值(当然也可以枚举b的值),然后因为c至少为a+8*j+1,那么对于一对j和a:
num[a][0]+=sum[a+8*j+1]*ha[a+2*j]
num[a+2*j][0]+=sum[a+8*j+1]*ha[a]
2.处理3,4
同理,但是sum定义要稍微改变一下,要倒着来,sum[i]表示<=i,差值为j的二元组有几个。然后枚举d的值(当然也可以枚举c的值),然后因为d至少为a+9*j+1,那么对于一对j和d:
num[d][0]+=sum[d-7*j-1]*ha[d-j]
num[d-j][0]+=sum[d-7*j-1]*ha[d]
然后还是一如既往地限制枚举范围,减少枚举次数,这就不阐述了,根据代码参悟吧。
期望得分:100分
#include
#include
const int maxn=15005,maxm=40005;
int n,m,a[maxm],ha[maxn],num[maxn][4],sum[maxn];
bool Eoln(char ch) {return ch==10||ch==13||ch==EOF;}
inline int readi(int &x) //读入优化
{
int tot=0,f=1;
char ch=getchar();if (ch==EOF) return 2;
while ('9''0') {if (ch=='-') f=-f;ch=getchar();}
while ('0'<=ch&&ch<='9') tot=tot*10+ch-48,ch=getchar();
x=tot*f;
return Eoln(ch);
}
int main()
{
int i,j,MAX=0,MAXj;
freopen("magic.in","r",stdin);
freopen("magic.out","w",stdout);
readi(n);readi(m);
for (i=1;i<=m;i++)
{
readi(a[i]);ha[a[i]]++; //记录a[i]的个数
if (a[i]>MAX) MAX=a[i];
}
if (n>MAX) n=MAX;MAXj=(n-2)/9; //MAXj按照d来算就行了,a越小且d越大j才能越大,那么a取1代入d,d当成n即可
for (j=1;j<=MAXj;j++) //枚举j
{
memset(sum,0,sizeof(sum)); //将sum清零
for (i=n-j;i>=1;i--) sum[i]=sum[i+1]+ha[i]*ha[i+j]; //记录sum
for (i=1;i<=n-9*j-1;i++) num[i][0]+=sum[i+8*j+1]*ha[i+2*j],num[i+2*j][1]+=sum[i+8*j+1]*ha[i]; //累计num
}
for (j=1;j<=MAXj;j++)
{
memset(sum,0,sizeof(sum));
for (i=2*j;i<=n;i++) sum[i]=sum[i-1]+ha[i-2*j]*ha[i];
for (i=9*j+2;i<=n;i++) num[i-j][2]+=sum[i-7*j-1]*ha[i],num[i][3]+=sum[i-7*j-1]*ha[i-j];
}
for (i=1;i<=m;i++) printf("%d %d %d %d\n",num[a[i]][0],num[a[i]][1],num[a[i]][2],num[a[i]][3]); //输出作为a,b,c,d的次数
return 0;
}